Qt的信号与槽(Signals and Slots) 是Qt框架最核心、最具特色的机制之一,用于实现对象之间的类型安全、松耦合通信。它替代了传统的回调函数或观察者模式,提供了一种更安全、灵活且易于使用的事件驱动编程模型。

1. 认识信号和槽

Qt使用信号和槽取代回调函数实现对象之间的通信。当一个特定事件触发时会发射信号,与此信号相关联的函数会被调用,该函数称之为槽或槽函数,这种关联Qt有专门的函数connect()实现,通过connect()函数将信号和槽连接起来,实现对象间的通信。因此信号和槽系统有三部分组成:信号、槽以及连接组成。

你可以将任意多个信号连接到同一个槽,也可以将一个信号连接到任意多个槽。甚至还可以将一个信号直接连接到另一个信号(这样,每当第一个信号被发射时,第二个信号会立即被触发)。

所有继承自QObject或其子类(例如QWidget)的类都可以包含信号和槽。也就是说只有QObject及其子类才可以使用信号和槽,由于信号和槽是通过元对象系统实现的,因此子类中必须包含Q_OBJECT宏。

connect(object_sender, signal, object_receiver, slot)

信号与槽是松耦合的:一个发出信号的类既不知道、也不关心有哪些槽接收了该信号,槽也不知道是否有任何信号连接到了它。信号和槽可以接受任意数量、任意类型的参数,且完全保证类型安全。这种设计确保了使用 Qt 能够创建真正相互独立的组件。

在这里插入图片描述

1.1 信号

信号是一个公共访问函数,可以从任何地方发出,建议从定义该信号的类及其子类中发出信号。Qt类特别是QWidget中有很多默认的信号,我们也可以自定义信号。

class MySignalObject : public QObject { //必须继承自QObject或其子类
	Q_OBJECT   //必须要有Q_OBJECT宏
signals:
	void signal1();  //无参数信号
Q_SIGNALS:
	void signal2(int);  //带参数的信号,参数可以是任意类型和数量
	void signal3(int, int);
public:
	Q_SIGNAL void signal4(const MyType&);
};

发射信号可以使用emit 信号()语法方式,如emit signals2(12), 如果需要在第三方源码中使用信和槽,例如Boost中,为了防止signals, slotsemit关键字冲突,我们可以在编译器中配置选项(在CMake项目中添加:target_compile_definitions(my_app PRIVATE QT_NO_KEYWORDS), 在qmake项目(.pro)文件中添加:CONFIG += no_keywords),告诉Qt不要定义moc关键字:signals, slotsemit,如果想继续使用信号和槽,我们可以用宏Q_SIGNAL(或Q_SIGNALS)替换signalsQ_SLOT(或Q_SLOTS)替换slotsQ_EMIT替换emit, 也可以使用#undef取消signals, slotsemit宏定义:

#undef signals
#undef slots
#undef signals

Q_SIGNALS (带S): 用于定义信号区域,Q_SIGNAL(不带S): 用于声明单个信号,类似Q_SLOTQ_SLOTS用于单个和多个槽函数。

不管是第三方还是非第三方源码中,不管有没有配置no_keywords,我们都可以使用宏Q_SIGNAL(或Q_SIGNALS)替换signalsQ_SLOT(或Q_SLOTS)替换slotsQ_EMIT替换emit, 而且为了防止可能与第三方库如Boost或C++标准库产生命名冲突,遵循Qt命名规范,Qt推荐使用Q_SIGNAL, Q_SLOTQ_EMIT, 这样在多库环境中更安全可靠。

1.2 槽

当与槽连接的信号被触发时,该槽会被调用。槽是普通的C++函数,可以像普通函数一样被直接调用;它们唯一