信号槽原理
在Qt
中,提供了信号槽来处理对象间的通信。信号槽机制是Qt
中的主要特征,同时也是Qt
不同于其它框架的最大特点。
信号槽有些类似windows中的消息机制,通过回调函数来处理对象间的通信。但是回调函数是一种紧耦合的设计,调用者必须知道该在什么时候调用哪个回调函数。
Qt
中的信号槽机制主要有以下两类函数实现:
- 信号函数:只管发出信号,不需要知道信号的接收者。例如按钮点击事件。
- 槽函数:只管接收,不管是谁发出的信号。例如响应一个点击事件。
信号函数与槽函数之间的联系通过QObject
对象来绑定。
Qt
信号槽的运行原理大致如下:
- 绑定信号函数和槽函数。
- 调用信号函数,发出信号(将信号写入队列中)
- 主线程从队列中获取信号。
需要注意的是,由于主线程是直接从队列中获取信号,所以信号的响应是依次执行的。当进行网络编程等响应时间过长的处理时,应该将信号的处理放到新线程中执行,否则会阻塞主线程。
通过Qt设计器(Qt designer)设置信号槽
拖拽添加信号槽
首先,我们打开Qt设计器界面,拖动一个按钮到界面中,并命名为关闭窗口,我们希望点击这个按钮可以关闭当前窗口。
然后选中下图所示的编辑信号槽按钮(左上角第二个图标)
选中按钮,按住左键将其拖到窗体边缘。在弹出的对话框中选择
clicked()
事件,勾选下方的显示从Qwidget继承的槽
,选中close()
事件。
通过上图,我们可以看到,pushButton
作为信号发出的对象,发出一个clicked()
信号。MainWindow
作为接收的对象,执行close()
操作。然后单击
OK
编译运行后,我们单击该按钮即可关闭窗口。
信号槽编辑器添加
除了上面的拖拽添加信号槽,我们还可以在Qt设计器中的信号槽编辑器中添加信号槽。
同样我们再拖动一个按钮到界面设计器中,并命名为最大化,通过单击该按钮实现最大化窗口。
添加一个按钮,并将对象名字改为max
,如上图右边所示。
然后在信号槽编辑器中添加如下规则:
其中,发送者为max
按钮,发送clicked()
信号,接收者为MainWindow
,响应的槽为showMaximized()
。
通过上面的信号槽编辑器不难发现,拖拽的添加方法和信号槽编辑器添加是一样的。
通过代码绑定信号槽
通过界面设计器,我们可以很方便的添加信号槽,但是在实际应用中,我们往往需要自己定义要发送的信号以及处理的槽函数,此时就需要通过代码来实现信号以及槽函数,并对他们进行绑定。
Q_OBJECT
对于所有需要添加信号槽的类,我们都需要定义Q_OBJECT
这个宏,这个宏本身没有任何作用,只是用来告诉moc
程序,下面来对比以下添加和不添加Q_OBJECT
宏的编译输出。
未添加Q_OBJECT
时的编译输出如下:
添加Q_OBJECT
后编译输出如下:
我们可以看到,在添加了Q_OBJECT
后编译输出多了moc_*
的内容。
信号函数
我们在mainwindow.h
中新增加一个信号函数mySignal()
,我只要定义这个信号就可以了,不需要去实现它,moc
程序会帮我们实现。
需要注意的是,信号函数需要有signals:
声明。
1 |
|
编译后,我们可以在moc_mainwindow.cpp
文件中找到如下内容,moc
程序帮我们完成了信号的定义,并可以将其加入到消息队列中。
1 | // SIGNAL 0 |
槽函数
下面我们来编写一个槽函数。槽函数可以申明多种访问权限,这里我们指定为public
。
1 |
|
需要注意的是,信号函数会自动生产定义,但是槽函数不会,槽函数需要我们自己添加定义。
我们实现槽函数的定义如下:
1 | void MainWindow::mySlots(){ |
通过控制台输出mySlots()
,此时需要在pro文件中增加CONFIG += console
来打开控制台。
信号槽的绑定
界面绑定
我们可以在Qt设计器中通过拖拽来绑定事件,但是没有显示我们的槽函数,我们需要手动添加槽函数。
完成后,可以看到如下的结果:
可以看到,信号函数和槽函数绑定成功,槽函数响应了我们的点击事件。
手动添加槽函数
上面演示了用Qt设计器绑定信号槽,下面通过代码来绑定信号槽。
通过观察上面生成的ui_mainwindow.h
文件,可以看到程序为我们绑定了信号槽,并且所有的界面元素对象以及样式都在该文件中进行了定义。
1 | QObject::connect(pushButton, SIGNAL(clicked()), MainWindow, SLOT(mySlots())); |
- 第一个参数
pushButton
为信号的发出者 - 第二个参数
SIGNAL(clicked())
表示clicked()
信号 - 第三个参数
MainWindow
为接收者 - 第四个参数
SLOT(mySlots())
为定义的槽函数
需要注意的是,QObject
为多有Qt对象的基类,正常可以不写,但当进行socket编程时需注意区分开来。
下面我们手动编写信号槽的连接。
1 | MainWindow::MainWindow(QWidget *parent) |
ui->pushButton
为信号发出对象SIGNAL(clicked())
表示clicked()
信号this
表示当前窗体为信号接收者SLOT(mySlots())
为定义的槽函数
除了上面的写法,信号槽函数还可以写成如下形式:
1 | QObject::connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::mySlots); |
总结
- 信号函数只需要声明,不用定义。
- 槽函数需要声明且定义。
- 一个信号可以绑定多个槽函数