QT的信号槽
在大多数Qt的编程中,我们通过Qt信号槽机制来对鼠标或键盘在界面上的操作进行响应处理,例如鼠标点击按钮的处理。Qt中的控件能够发出什么信号,在什么情况下发射信号,这在Qt的文档中有说明,每个不同的控件能够发射的信号种类和触发时机也是不同的。
如何为控件发射的信号指定对应的处理槽函数呢,我们有两种方式,第一种是在UI设计界面上操作:
在按钮控件上点击右键,选择“转到槽”菜单之后弹出如下的对话框:
可以看到按钮控件会发射很多信号,只要选择一个信号,点击OK之后就会生成对应的槽函数对按钮发出的该信号进行处理
1
2
3
|
void MainWindow::on_btnTest_clicked() { } |
选择clicked()信号之后生成的处理该信号的槽函数,除了通过UI界面自动生成槽函数的方式以外,我们还可以在代码中自己手写槽函数,并通过QObject::connect()函数将特定对象的信号与另外一个对象的槽函数进行连接,当该对象的信号发射之后,会被关联的对象的槽函数处理。例如我们可以用下面的一行代码完成上面的功能:
1
|
connect(ui->btnTest,SIGNAL(clicked()), this ,SLOT(on_btnTest_clicked())); |
使用代码的好处是,很多控件的信号在上面的对话框中并没有显示出来,也就是说上面的对话框中其实只列出了该控件对象的一部分信号,另外如果我们的对象是在程序中通过代码动态构建的,那么我们也就需要在代码中为该控件的信号指定处理的槽函数了。上面的connect代码是我们直接在UI界面类的构造函数中写的(当然在任何地方都可以,并不一定要在构造函数中),由于UI界面类也是继承自QObject所以自然也继承了connect函数,通过connect函数我们可以将一个对象的信号与另一个对象的槽函数进行连接,当个该对象的信号发射的时候(信号的发射时机有可能在代码中调用对象的某个成员函数触发,也有可能在程序的UI界面上操作鼠标,键盘等触发)。
另外信号与槽在通过connect函数连接的时候,其参数类型必须完全一致,否则是没有效果的。实际上信号槽的原理,是依赖于Qt的元对象系统,Qt的一系列的构建工具为程序员做了很多自动化的工作,自动生成了一些代码,所以使得我们看起来只需要用connect函数进行关联之后,在信号发射的时候(通过emit发射信号),槽函数会被自动调用。在我们的Qt的项目的debug目录下,我们往往会看到很多以moc_为前缀的cpp文件,打开这些文件我们就可以看到该文件中的qt_meta_data_为前缀的静态数组里面描述了信号槽的关联信息,而在qt_static_metacall函数的实现中,我们可以大致看到通过一系列的case分支,对应的槽函数被调用。如果要详细研究Qt的信号槽的实现原理,可以研究QObject类的源码,以及Qt的元对象系统。
槽函数被slots修饰,当然它可以是普通的成员函数。信号被signals修饰。一个信号可以关联多个槽函数,当信号被发射的时候,这些槽函数依次被执行,但是执行的顺序是未知的,一个槽函数可以被多个信号关联。一个信号也可以关联另外一个信号,当该信号被发射的时候,与它关联的信号也被发射。通过disconnect函数可以取消信号与槽函数之间的关联关系。在槽函数中直接调用sender()就可以获得触发该槽函数的信号源对象,该函数是QObject的成员函数,返回的也是一个QObject类型的指针。
另外信号槽可以在不同的线程之间使用,但是使用的时候需要注意调用connect时候指定连接的方式,不同的线程之间Qt可以通过消息队列来实现信号与槽函数的关联,我经常在UI线程中关联另外一个工作线程的信号到UI界面类中的成员函数,以便在工作线程中通过发送信号的方式来调用UI主线程中的UI界面类的成员函数,来达到更新UI界面的效果。Qt中不能在工作线程中直接对UI界面控件进行操作。有关信号的连接方式可以参考这篇文章:对信号与事件的认识(http://blog.chinaunix.net/uid-25147458-id-3706122.html)