在图形界面编程中(参考《C++最好的图形库是什么?》),组件之间如何实现通信是核心的技术内容。Qt 使用了信号与槽的机制,非常的高效、简单、易学,方便开发者的使用。本文详细的介绍了Qt 当中信号与槽的概念,并演示了各种信号与槽的连接方式。

一、什么是信号和槽(Signal and Slot)

信号和槽是用于对象之间的通信,它是Qt的核心机制,在Qt编程中有着广泛的应用。如果想学好Qt,一定要充分掌握信号的槽的概念与使用。关于Qt的介绍可以

举个例子,在一个十字路口,信号灯变成了绿色,对面的汽车看到后就启动了。信号灯就是发送信号的对象,绿灯亮是它发送的信号 (signal),汽车是接收对象,汽车行驶是汽车对信号的响应,也叫槽 (slot)。

再举一个例子,比如在一个主窗口内有一个关闭按钮,如果点击这个按钮窗口就会关闭,那么关闭按钮是发送信号的对象,它发送的信号是点击,接收信号的对象是窗口,响应信号的槽是关闭窗口。

二、信号和槽的代码实例

在Qt中,发送对象、发送的信号、接收对象、槽可以通过很多种方式连接。我们下面通过一些例子逐一做演示。

(1)Qt 4 使用宏

在Qt 4的版本中,主要通过connect + 宏的方式进行通信连接。

connect(发送对象,信号,接收对象,槽函数),其中发送信号和槽函数需要用 SIGNAL() 和 SLOT() 来进行声明。

connect 函数声明如下:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

比如点击按钮关闭窗口的例子,代码可以这样写:

connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));

如果想自定义槽函数,需要先将槽函数的声明添加到类的slots中。比如我们对一个QLineEdit控件添加一个接收textEdited信号的槽函数onTextEdited

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow(); private slots:
void onTextEdited(QString); private:
Ui::MainWindow *ui;
};

然后实现函数,并用connect与信号连接

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));
connect(ui->lineEdit, SIGNAL(textEdited(QString)), this, SLOT(onTextEdited(QString)));
} void MainWindow::onTextEdited(QString s)
{
qDebug() << s;
}

这样写的好处是信号和槽参数很直观,但缺点是因为使用宏,编译时不做类型检查,如果有问题的话,在运行的时候才会发现。

(2)使用Qt Creator 界面添加信号的槽函数

另外一种方式不需要使用 connect 函数,可以通过Qt Creator 界面来完成发送信号和槽函数的连接,比如我们右键点击一个按钮,然后选择“转到槽”:

选择信号,我们点击QAbstractButton的clicked()信号,表示按钮被点击:

接下来,Qt Creator会自动为我们生成如下代码,首先是槽函数的声明:

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow(); private slots:
void on_pushButton_clicked(); private:
Ui::MainWindow *ui;
};

然后是槽函数的实现:

void MainWindow::on_pushButton_clicked()
{ }

使用这种方法我们不需要使用connect函数将信号与槽函数做连接。 这里槽函数的命名有一定的规则,一般是 on_objectname_signal 这样来命名的。这种方法优点是减少了自己手动敲代码的工作量,缺点是究竟有哪些信号与槽函数做了连接不易被发现,没有connect 函数看起来直观。

(3)使用Qt 5 新 connect 函数

Qt 5 推出了新的 connect 函数,不需要使用 SIGNAL() 和 SLOT() 宏,可以在编译时做类型检查:

connect函数的声明如下:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

同样用代码实现点击按钮关闭窗口,并且添加一个QLineEdit控件,发送textEdited信号,由onTextChanged()函数作为槽函数响应。

使用这种方法槽函数的声明不需要放到slots中,只要像普通的函数一样声明就可以了,类型需要与textEdit信号保持一致

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void textChanged(QString); private:
Ui::MainWindow *ui;
};

使用 connect 将信号与槽函数连接,不需要再使用 SIGNAL() 和 SLOT() 宏

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::close);
connect(ui->lineEdit, &QLineEdit::textEdited, this, &MainWindow::textChanged);
} void MainWindow::textChanged(QString s)
{
qDebug() << s;
}

(4)使用函数指针

在Qt 5版本的connect 函数里,信号与槽函数的参数其实都是函数指针,当信号或槽函数有重载时,使用函数指针可以明确告诉编译器使用哪一个重载函数,避免歧义。下面的例子虽然没有使用重载,不过我们改成通过使用函数指针来向connect传递槽函数参数。

首先还是声明两个槽函数,分别响应点击按钮信号,和textEdited信号:

class MainWindow : public QMainWindow
{
Q_OBJECT public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void onButtonPushed();
void onTextEdited(QString); private:
Ui::MainWindow *ui;
};

然后对函数做简单的实现:

void MainWindow::onButtonPushed()
{
this->close();
} void MainWindow::onTextEdited(QString s)
{
qDebug() << s;
}

最后声明函数指针,并且将它们与信号连接

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
void(MainWindow:: *buttonClickSlot)() = &MainWindow::onButtonPushed;
void(MainWindow:: *textEditedSlot)(QString) = &MainWindow::onTextEdited;
connect(ui->pushButton, &QPushButton::clicked, this, buttonClickSlot);
connect(ui->lineEdit, &QLineEdit::textEdited, this, textEditedSlot);
}

(5)使用Lambda表达式

使用 Lambda表达式的好处是代码的书写更加方便快捷。在connect 函数中,槽函数参数我们可以改用Lambda表达式的方式来进行传参。

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
this->close();
});
connect(ui->lineEdit, &QLineEdit::textEdited, this, [=](QString s){
qDebug() << s;
});
}

使用Lambda表达式,我们就不需要在类中对槽函数做任何的声明了。Lambda表达式是C++ 11的内容,在比较低的 Qt版本中,要注意在Pro项目文件中加入 CONFIG += C++ 11。

三、总结

Qt 当中组件之间通过信号与槽的方式进行通信非常地高效,对于开发者来说也很简单。使用 Qt 5版本的开发者建议使用上面后三种新的方式进行连接。补充一点,信号和槽之间不是一一对应的关系。一个信号可以对应多个槽,比如点击一个按钮可以触发多个不同的响应;一个槽也可以响应多个不同的信号,比如点击按钮可以关闭窗口,点击左上角的小叉也可以关闭窗口。信号和槽之间只要通过connect 函数连接就建立了耦合关系,如果想解除连接可以使用disconnect 函数。

推荐阅读:

C++最好的图形库是什么?

Linux快速搭建C/C++开发环境

一篇文章快速搞懂什么是GitHub

获取知识干货、增加面试经验、了解职场人生

欢迎关注微信公众号

Qt信号与槽使用方法最完整总结的更多相关文章

  1. QT 信号与槽connect

    QT 信号与槽connect QT 信号与槽connect connect函数调用几个限制 connect函数代码 QT中信号与槽的连接使用的connect函数是一个静态函数,在类QObject中定义 ...

  2. Qt 信号与槽

    Qt信号与槽的理解 信号和槽机制是 QT 的核心机制,要精通 QT 编程就必须对信号和槽有所了解.信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重 ...

  3. QT信号和槽

    QT信号和槽 ============ 信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性.要正确的处理信号和槽,必须借助一个称为 moc(Meta Object Compiler) ...

  4. Qt信号与槽传递自定义数据类型——两种解决方法

    信号与槽作为qt中的核心机制,在qt应用开发中经常会用的,但是原生的信号与槽连接传参,只支持基本的数据类型,比如char,int, float,double. 如果想要在信号与槽之间传递自定义参数,比 ...

  5. Qt信号与槽自动关联机制

    参考链接1:http://blog.csdn.net/skyhawk452/article/details/6121407 参考链接2:http://blog.csdn.net/memory_exce ...

  6. 关于Qt信号与槽机制的传递方向性研究(结论其实是错误的,但是可以看看分析过程)

    最近由于项目的需求,一直在研究Qt.信号与槽机制是Qt的一大特色,该机制允许两者间传递参数,依次来实现对象间的通信.这个参数会分别存在于信号的参数列表和槽函数的参数列表中.需要注意的是,若将槽函数绑定 ...

  7. QT信号和槽在哪个线程执行问题

    时隔四个月后的第一篇,换了个公司可以登录的博客,记录一些学习内容吧 这是看到别人写的比较好的一篇,排版有点乱 QThread的使用方法 起源 昨天不小心看到Qt开发人员( Bradley T.Hugh ...

  8. Qt信号与槽机制

    一.信号和槽机制 信号和槽用于两个对象之间的通信,信号和槽机制是Qt的核心特征,也是Qt不同于其他开发框架的最突出的特征.在GUI编程中,当改变了一个部件时,总希望其他部件也能了解到该变化.更一般来说 ...

  9. [QT][转载] Qt信号和槽

    From: http://blog.csdn.net/rl529014/article/details/51346955 GUI 程序除了要绘制控件,还要响应系统和用户事件,例如重绘.绘制完成.点击鼠 ...

随机推荐

  1. R星游戏如何绑定二次验证码_虚拟MFA_两步验证_谷歌身份验证器?

    一般点账户名——设置——安全设置中开通虚拟MFA两步验证 具体步骤见链接 R星游戏如何绑定二次验证码_虚拟MFA_两步验证_谷歌身份验证器? 二次验证码小程序于谷歌身份验证器APP的优势 1.无需下载 ...

  2. Promise对象 异步编程

    Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是 ...

  3. 简单理解:数据库的一致性与四种隔离级别(+MySQL实现)

    并行数据库存在着几种常见不一致问题: 1.更新丢失:两个并发的写进程同时修改某内容,一个没修改完提交之后另一个又提交,导致其覆盖了第一个提交的写进程内容. 2.脏读:一个操作读到了另外一个操作没有提交 ...

  4. Dockerfile镜像优化,减小镜像

    前言镜像的优化注意几条: 选择最精简的基础镜像减少镜像的层数清理镜像构建的中间产物注意优化网络请求尽量去用构建缓存使用多阶段构建镜像接下来我们以rhel7镜像构建容器,并在容器中安装nginx的源码包 ...

  5. IO—》打印流&commons-IO

    打印流 打印流添加输出数据的功能,使它们能够方便地打印各种数据值表示形式. 打印流根据流的分类: 字节打印流 PrintStream 字符打印流 PrintWriter 方法: void print( ...

  6. 《Python Web开发实战》|百度网盘免费下载|Python Web开发

    <Python Web开发实战>|百度网盘免费下载|Python Web开发 提取码:rnz4 内容简介 这本书涵盖了Web开发的方方面面,可以分为如下部分: 1. 使用最新的Flask ...

  7. Python os.fchdir() 方法

    概述 os.fchdir() 方法通过文件描述符改变当前工作目录.高佣联盟 www.cgewang.com Unix, Windows 上可用. 语法 fchdir()方法语法格式如下: os.fch ...

  8. CF掉分日记 6.6 6.8

    ---恢复内容开始--- 写的效果依旧不好 还没写完前四题比赛就结束了 而且这些普及组的题目 我大多还是缺少简单算法的灵性 总是把问题搞复杂化. 6.5 A 第一道题非常水 简单分析发现是一个快速幂的 ...

  9. Python爬取10000条“爆款剧”——《三十而已》热评,并做可视化

    前言 继<隐秘的角落>后,又一部“爆款剧”——<三十而已>获得了口碑收视双丰收,王漫妮.顾佳.钟晓芹三个女主角的故事线频频登上微博热搜.该剧于2020年7月17日在东方卫视首播 ...

  10. PHP代码混淆与加密——php screw plus

    php是一个开源的.广受欢迎的语言,php应用常常是以代码明文的发式发布,但是有时候对于发布的代码我们想要进行保护,需要对php代码进行混淆与加密,让增加破解者的逆向难度,从而达到保护自己成果的目的. ...