信号与槽的概念
在Qt框架中,信号(Signal)与槽(Slot)是用于实现对象间通信的一种机制。它们主要用于响应事件,比如点击一个按钮会弹出一个提示。点击按钮就是事件,弹出提示就是响应这个事件的行为。
信号与槽机制是Qt框架中实现事件驱动编程的核心。它们提供了一种松耦合的方式,使对象间的通信变得简单高效。通过定义信号和槽并将它们连接起来,可以轻松地处理各种用户交互和事件响应。
- 信号:信号是一种用来通知某些事件发生的机制。对象可以发出信号来通知其他对象某些特定的事件已经发生了。
- 槽:槽是一个用来响应信号的普通成员函数。槽的定义类似于普通成员函数,只不过用
slots
关键字标识。槽函数可以执行任何操作。
要想让信号与槽之间起作用,我们需要使用connect
函数将其连接起来。该连接操作通常在类的构造函数中完成,这样在特定的类对象生成的时候就会自动连接信号和槽函数。
connect函数
给出connect函数常见声明的几种方式:
旧的字符串形式
旧版本的connect函数的用法是基于字符串的。如下
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method);
sender
表示发送信号的对象signal
表示发送信号的类型,比如鼠标点击信号等receiver
表示接收信号的对象method
表示响应信号的方式
总的来说,connect函数完成了一个信号与槽的连接,回答了四个问题:谁发送的信号?发送了什么信号?发送信号给谁?怎么处理这个信号?
值得注意的是,在早期的connect函数的参数类型中,形参signal与method是一个char类型的指针,这就导致不同类型的指针要想传参就必须要进行类型转换。尤其是method作为一个函数指针,跟实际上要传参的函数指针会有较大的差异。 于是Qt设计了SIGNAL和SLOT,这两个都是宏,分别用来在使用connect传参时signal与method的类型转换。具体使用方式如下:
connect(senderObject, SIGNAL(signalName(type1, type2)), receiverObject, SLOT(slotName(type1, type2)));
此外,旧版本的槽函数也必须要声明在private slots之后,比如
在Qt5出来之后就不做硬性要求了。(slots是qt自己扩展的关键字)
我们发现其实这样传参特别麻烦。所以Qt5出来之后,把connect函数做成了一个函数模板,在实例化模板函数的时候就会自行检查传参的正确性:
基于函数指针的形式
template <typename Func1, typename Func2> static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection);
使用方式如下:
connect(senderObject, &SenderClass::signalName, receiverObject, &ReceiverClass::slotName);
示例:
上述代码设计了触发提示的按钮,点击按钮之后就会弹出 "Button clicked! "
基于Lambda表达式作为槽
Qt5及以后的版本支持使用lambda表达式作为槽函数,使得代码更加简洁和灵活。
QObject::connect(&button, &QPushButton::clicked, []() { qDebug("Button clicked!"); });
在这个示例中,当按钮被点击时,lambda表达式会被执行,输出“Button clicked!”。
总结
connect函数是Qt框架中一个核心的函数,用于连接信号和槽。Qt提供了多个connect函数的重载版本,用来适应不同场景的需求。
旧的字符串形式缺乏类型安全,不推荐适应使用。新的基于函数指针的形式和更加安全和简洁。推荐在Qt5及以后的版本中使用。此外,lambda表达式作为槽提供了更多的灵活性和简洁性。