Qt | 信号和槽的一些总结
前言:
信号和槽用于两个对象之间的通信,信号和槽机制是 Qt 的核心特征。在使用信号和槽时,一个信号可以关联到多个槽上,多个信号也可以关联到同一个槽上,一个信号还可以关联到另一个信号上。如果是一个信号关联多个槽,这些槽会一个接一个地执行,执行顺序与关联顺序相同。
之前遇到一个面试题:
如果同一个信号和同一个槽函数进行了多次连接(connect),是否会造成崩溃?
答案是不会,如果同一个信号和同一个槽函数进行多次连接,那么信号在发出后,会多次执行槽函数。
信号和槽详解:
声明一个信号需要使用 signals 关键字,在 signals 前面不能添加 public、private、protected 等限定符。信号默认是 public 的,可以在任何地方进行发射(emit),但是建议只在定义该信号的类及其子类中进行发射改信号。
信号只用声明,不需要实现。而且,信号没有返回值,只能是 void 类型的。
只有 QObject 类及其子类派生的类才可以使用信号和槽机制。使用信号和槽还必须在类声明的最开始处添加 Q_OBJECT 宏。
槽就是普通的 C++函数,可以像一般函数一样使用。声明槽要使用 slots 关键字,一个槽可以是 private、public 或者 protected 类型的,也可以被声明为虚函数。
槽函数中的参数的类型和信号参数的类型要相对应,且不能比信号的参数多。
connect 详解:
第一个参数为发射信号的对象。
第二个参数为要发射的信号。
第三个参数为接收信号的对象。
第四个参数为要执行的槽。
第五个参数 type 为关联的方式。
Qt::AutoConnection: 自动关联,默认值。如果 receiver 于发射信号在同一线程,则该类型表示 Qt::DirectConnection;否则,该类型表示 Qt::QueuedConnection。在信号被发射时决定表示那种类型。
Qt::DirectConnection: 直接关联。发射完信号后立即调用槽函数,只有槽函数执行完成返回后,发射信号后面的代码才会执行。
Qt::QueuedConnection: 队列关联。当控制返回 receiver 所在线程的事件循环后再执行槽函数,无论槽函数是否执行,发射信号后面的代码都会立即执行。
Qt::BlockingQueuedConnection: 阻塞队列关联。类似 Qt::QueuedConnection,不过,信号线程会一直阻塞,知道槽函数返回。当 receiver 存在于信号线程时不能使用该类型,不然程序会死锁。
Qt::UniqueConnection: 唯一关联。这是一个标志,可以结合其他几种连接类型,使用按位或操作。这时两个对象间的相同的信号和槽只能有唯一的关联。使用这个类型主要是为了防止重复关联。
Qt5 之前的形式: connect(dlg, SIGNAL(funcSignal(int)), this, SLOT(funcSlot(int)));
Qt5 加入的一种形式: connect(dlg, &MyDialog::funcSignal, this, &Widget::funcSlot);
使用 Qt5 的这种形式的一个好处就是可以再编译时进行检查,信号或槽的拼写错误、槽函数参数数目多于信号的参数数目等错误都会在编译时发现。
另外,这种形式还支持 C++11 中的 Lambda 表达式,可以在关联时直接编写信号发射后要执行的代码,例如:
connect(dlg, &MyDialog::funcSignal, [=](int value){ui->label->setText(QString::number(value))});
信号和槽的自动关联:
槽函数:on_"部件的 objectName"_"信号名称"。
以这种方式命名的槽函数可以直接和信号关联,不用再使用 connect()函数。
connectSlotsByName()函数是用来支持信号和槽自动关联的,因为 setupUi()函数中自动调用了 connectSlotsByName()函数,所以要使用自动关联的部件的定义都要放在 setupUi()函数调用之前,且必须使用 setObjectName()指定它们的 objectName,只有这样才能正常使用自动关联。
断开关联 disconnect:
断开与一个对象多有信号的所有关联:disconnect(myObject, 0, 0, 0); 等价于 myObject->disconnect();
断开与一个指定信号的所有关联:disconnect(myObject, SIGNAL(mySignal()), 0, 0);等价于 myObject->disconnect(SIGNAL(mySignal()));
断开与一个指定的 receiver 的所有关联:disconnect(myObject, 0, myReceiver, 0); 等价于 myObject->disconnect(myReceiver);
断开一个指定信号和槽的关联:disconnect(myObject, SIGNAL(mySignal()), myReceiver, SLOT(mySlot())); 等价于 myObject->disconnect(SIGNAL(mySignal()), myReceiver, SLOT(mySlot()));还等价于 disconnect(myConnection);//myConnection 是进行关联时 connect()的返回值。
disconnect 也可以使用基于函数指针的重载形式,及 Qt5 后添加的不需要 SIGNAL()和 SLOT()宏的形式。
关于信号槽的高级应用:
Qt 提供了 QObject::sender() 函数来返回发送该信号的对象的指针。
有时存在多个信号关联到同一个槽上的情况,此时如果想在槽函数中对某个信号进行特殊处理,就可以通过 QObject::sender()来获取信号的对象指针,来进行判断是否是我们需要特殊处理的对象。
如果在多个信号关联到同一个槽上,而在该槽中需要对每一个信号进行不同的处理,可以使用 QSignalMapper 类。QSignalMapper 被叫做信号映射器,可以实现对多个相同部件的相同信号进行映射,为其添加字符串或者数值参数,然后再发射出去。
QSignalMapper 使用举例:
版权声明: 本文为 InfoQ 作者【YOLO.】的原创文章。
原文链接:【http://xie.infoq.cn/article/c950c6fd5df7daecd9b3cb5ff】。未经作者许可,禁止转载。
评论