Qt 信号槽系统
综述
信号和槽用于对象之间的通信。信号和槽机制是 Qt 的核心功能,可能也是与其他框架所提供的功能最不同的部分。
简介
在 GUI 编程中,当我们更改一个小部件时,我们通常希望通知另一个小部件。更一般地说,我们希望任何类型的对象都能够彼此通信。例如,如果用户单击 Close 按钮,我们可能希望调用窗口的 Close() 函数。
旧的工具包使用回调实现这种通信。回调是一个指向函数的指针,所以如果你想让一个处理函数通知你一些事件,你可以向处理函数传递一个指向另一个函数(回调)的指针。然后,处理函数在适当的时候调用回调。回调有两个根本缺陷:首先,它们不是类型安全的。我们永远不能确定处理函数是否会用正确的参数调用回调函数。其次,回调与处理函数强耦合,因为处理函数必须知道要调用哪个回调。
信号和槽
在 Qt 中,我们有另一种回调技术:我们使用信号和槽。当特定事件发生时,将发出一个信号。Qt 的小部件有许多预定义的信号,但我们总是可以子类化小部件,向它们添加我们自己的信号。槽是响应特定信号而调用的函数。Qt 的小部件有许多预定义的槽,但通常的做法是子类化小部件并添加自己的槽,这样您就可以处理感兴趣的信号。
信号和槽机制是类型安全的:信号的签名必须与接收槽的签名匹配。(事实上,一个槽的签名可能比它接收到的信号短,因为它可以忽略额外的参数。)由于签名是兼容的,编译器可以帮助我们检测类型不匹配。信号和槽是松散耦合的:发送信号的类既不知道也不关心哪个槽接收信号。Qt的信号和槽机制确保了如果你将一个信号连接到一个槽,槽将在正确的时间被信号的参数调用。信号和槽可以接受任何类型的任意数量的参数。它们是完全类型安全的。
从 QObject 或它的一个子类(例如 QWidget)继承的所有类都可以包含信号和槽。当对象以其他对象可能感兴趣的方式改变其状态时,就会发出信号。这就是对象用来通信的所有功能。它不知道也不关心是否有东西在接收它发出的信号。这是真正的信息封装,并确保对象可以作为软件组件使用。
槽可以用来接收信号,但它们也是正常的成员函数。就像一个对象不知道是否有任何东西接收到它的信号一样,一个槽也不知道是否有任何信号连接到它。这确保了可以用 Qt 创建真正独立的组件。
您可以将任意多的信号连接到一个槽,并且一个信号可以连接到任意多的槽。甚至可以将一个信号直接连接到另一个信号。(当第一个信号被发出时,它将立即发出第二个信号。)
信号和槽一起构成了一个强大的组件编程机制。
实例
最小的 C++ 类声明可能是这样的:
class Counter
{
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
void setValue(int value);
private:
int m_value;
};
一个小型的基于 QObject 的类可能是这样的:
#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);
sign