信号和插槽用于对象之间的通信。信号和槽机制是Qt的核心特性,可能也是与其他框架提供的特性最大不同的部分。Qt的元对象系统使信号和插槽成为可能。
作为一个C/C++程序员来说 回调应该属于深入人心的机制了。其他工具包使用回调来实现这种通信。回调函数是一个指向函数的指针,所以如果你想要一个处理函数通知你一些事件,你可以将一个指向另一个函数(回调函数)的指针传递给处理函数。处理函数然后在适当的时候调用回调函数。但回调可能不太直观,而且在确保回调参数的类型正确性方面可能会遇到问题。
在Qt中,我们有一个回调技术的替代方案:我们使用信号和槽。当特定事件发生时发出信号。Qt的小部件有许多预定义的信号,但是我们总是可以子类化小部件,添加一些自定义信号。槽(slots)是响应特定信号(signals)而调用的函数。Qt的小部件有许多预定义的槽,但是通常的做法是子类化控件并自定义槽函数,这样就可以处理感兴趣的信号。下图为信号与槽的关系图:支持一对多,多对一。
(1)信号和槽位机制是类型安全的:
信号的签名与接收的槽函数签名必须匹配。(事实上,槽函数的签名可能比它接收到的信号短,因为它可以忽略额外的参数。)由于签名是兼容的,所以当使用基于函数指针的语法时,编译器可以帮助我们检测类型是否匹配。基于字符串的信号和槽语法将在运行时检测类型不匹配。
(2)信号和插槽在软件层是解耦的:
发出信号的类既不知道也不关心哪个插槽接收信号。如果你将一个信号连接到槽函数,Qt的信号和插槽机制会确保立即调用携带信号传递过来的的参数的槽函数。信号和插槽可以接受任何类型的任意数量的参数,所以它们是完全类型安全的。
那什么是信号(signals)呢 ?
当object的内部状态以object的客户端或用户感兴趣的某种方式发生变化时比如点击、鼠标移动等,object就会发出信号。信号(signals)是公共访问函数,可以从任何地方发出,但最好只从该定义信号的类及其子类使用该信号。当信号发出时,通常采用直连方式连接槽函数,这种连接方式会立即执行槽函数,就像普通的函数调用一样。此时信号和槽机制完全独立于任何GUI事件循环。一旦所有的插槽都返回,emit语句之后的代码就会执行(同步发送)。当使用队列连接时,情况略有不同;在这种情况下,emit关键字后面的代码将立即继续,插槽将稍后执行(异步发送)。如果多个插槽连接到一个信号,当信号发出时,插槽将按照它们连接的顺序依次执行。信号是由moc(元对象编译器)自动生成的,不能在.cpp文件中实现。它们永远不能有返回类型(即使用void)。
static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,const QObject *receiver, const QMetaMethod &method,Qt::ConnectionType type = Qt::AutoConnection);
//以下为几种连接方式:
enum ConnectionType {
AutoConnection,
DirectConnection,
QueuedConnection,
BlockingQueuedConnection,
UniqueConnection = 0x80
};
那什么是槽(slots)函数呢?
slot是普通的c++函数,可以正常调用;它们唯一的特点是可以连接信号。因为槽函数是普通的成员函数,所以当直接调用时,它们遵循普通的c++规则。然而,作为Qt核心的机制槽函数,不管其访问级别,通过信号插槽连接,它们可以被任何组件调用。这意味着从任意类的实例发出的信号可能导致在不相关类的实例中调用私有插槽。与回调相比,信号和插槽稍微慢一些,因为它们提供了更大的灵活性,尽管实际应用程序中的差异并不大。一般来说,emit一个连接到一些插槽的信号,大约比直接调用非虚函数慢十倍。原因是在定位连接对象、安全遍历所有连接(即检查在发送过程中后续槽函数是否被销毁)以及以通用方式编排任何参数所需的开销。虽然十个非虚函数调用可能听起来很多,但它的开销比任何new或delete操作都要小得多。一旦在后续执行需要new或delete的字符串、vector or list等操作,信号和槽开销只占整个函数调用开销的很小一部分。当你在槽中执行系统调用时,情况也是一样的;或间接调用十多个函数。信号和插槽机制的简单性和灵活性是非常值得的,用户甚至不会注意到这些开销。
注意,当与基于qt的应用程序一起编译时,定义了称为signals或slots的变量的第三方库可能会导致编译器警告和错误。要解决这个问题,#undef + 错误的预处理器符号
在需要信号发送方信息的情况下,Qt提供了QObject::sender()函数,它返回一个指向发送信号的对象的指针。用法如下:
void MyWidget::on_m_cutPushButton_clicked()
{
QPushButton *push = static_cast<QPushButton*>(QObject::sender());
qDebug() << push->text(); //
}
refence to https://doc.qt.io/qt-5/signalsandslots.html