信号和插槽
在 GUI 编程中,当我们更改一个小部件时,我们经常希望通知另一个小部件。更一般地说,我们希望任何类型的对象都能够相互通信。
其他工具包使用回调实现这种通信。
在 Qt 中,我们有一个回调技术的替代方案:我们使用信号和槽。
信号和槽机制是类型安全的:信号的签名必须与接收槽的签名匹配。
基于字符串的 SIGNAL 和 SLOT 语法将在运行时检测类型不匹配。信号和槽是松散耦合的:发出信号的类既不知道也不关心哪个槽接收信号。
Qt 的信号和槽机制确保如果将信号连接到槽,则将在正确的时间使用信号的参数调用槽。
从 QObject 或其子类之一(例如 QWidget)继承的所有类都可以包含信号和槽。
槽可以用来接收信号,但它们也是普通的成员函数。就像一个对象不知道是否有任何东西接收到它的信号一样,一个槽也不知道它是否有任何信号连接到它。
您可以将任意数量的信号连接到单个插槽,并且可以将信号连接到任意数量的插槽。甚至可以将一个信号直接连接到另一个信号。 (这将在第一个信号发出时立即发出第二个信号。)
信号是公共访问函数,可以从任何地方发出,但我们建议只从定义信号的类及其子类中发出它们。
当一个信号发出时,连接到它的槽通常会立即执行,就像一个普通的函数调用一样。发生这种情况时,信号和槽机制完全独立于任何 GUI 事件循环。一旦所有槽都返回,将在发出语句之后执行代码。使用排队连接时情况略有不同;在这种情况下,emit 关键字后面的代码将立即继续执行,而 slot 将在稍后执行。
如果多个槽连接到一个信号,则在发出信号时,这些槽将按照它们连接的顺序一个接一个地执行。
插槽
插槽是普通的C++函数,可以正常调用;它们唯一的特点是可以将信号连接到它们。
与回调相比,信号和槽的速度稍慢,因为它们提供了更高的灵活性
一般来说,发出一个连接到某些槽的信号,比直接调用接收器慢大约十倍
请注意,定义称为信号或槽的变量的其他库在与基于 Qt 的应用程序一起编译时可能会导致编译器警告和错误。为了解决这个问题,#undef 有问题的预处理器符号。
所有包含信号或槽的类都必须在其声明的顶部提及 Q_OBJECT。它们还必须(直接或间接)从 QObject 派生。
默认情况下,对于您建立的每个连接,都会发出一个信号;为重复连接发出两个信号。您可以通过一次 disconnect() 调用中断所有这些连接。如果您传递 Qt::UniqueConnection 类型,则只有在不重复时才会建立连接。如果已经存在重复(完全相同的信号到相同对象上完全相同的插槽),则连接将失败并且连接将返回 false。
例子
class Counter
{
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
void setValue(int value);
private:
int m_value;
};
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
};
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
调用 a.setValue(12) 使 a 发出 valueChanged(12) 信号,b 将在其 setValue() 槽中接收该信号,即调用 b.setValue(12)。然后 b 发出相同的 valueChanged() 信号,但由于没有插槽连接到 b 的 valueChanged() 信号,因此该信号被忽略。
使用lambda表达式
connect(action, &QAction::triggered, engine,
[=]() { engine->processAction(action->text()); });