自定义信号与槽
Qt 中自定义信号与槽
在 Qt 中,信号与槽不仅可以使用框架内置的接口,也可以由开发者自定义。这使得 Qt 可以在对象之间构建 灵活、解耦的通信机制。在实际项目中,自定义信号与槽经常用于 业务逻辑通知、模块间通信以及状态变化广播。
例如:
- 网络模块收到数据 → 通知 UI 更新
- 后台任务完成 → 通知主线程刷新界面
- 某个业务状态变化 → 通知多个模块响应
这些场景都非常适合通过 自定义信号与槽 实现。
一、自定义信号与槽的基本语法
Qt 允许开发者在类中声明自己的信号和槽,但需要遵循一定的规范。
1 自定义信号
信号需要在 signals 关键字下声明,例如:
1 2 3 4 5 6 7 8 9 10
| class Teacher : public QObject { Q_OBJECT
public: explicit Teacher(QObject *parent = nullptr);
signals: void classBegin(); };
|
信号的特点:
- 必须写在
signals 区域中
- 返回值必须为
void
- 只声明,不需要实现
- 可以带参数
- 可以重载
例如:
1 2 3
| signals: void classBegin(); void classBegin(QString courseName);
|
信号函数实际上 不会由开发者实现。Qt 在编译阶段通过 MOC(Meta Object Compiler) 自动生成相关代码。
2 自定义槽
槽本质上就是普通成员函数,用于响应信号。
早期 Qt 版本要求槽必须写在:
但在 Qt5 之后,这个限制已经放宽,槽函数可以写在:
1 2 3
| public protected private
|
例如:
1 2 3 4 5 6 7 8 9 10
| class Student : public QObject { Q_OBJECT
public: explicit Student(QObject *parent = nullptr);
public slots: void startStudy(); };
|
槽函数特点:
- 需要声明并实现
- 返回值通常为
void
- 可以带参数
- 可以重载
- 可以像普通函数一样被调用
实现示例:
1 2 3 4
| void Student::startStudy() { qDebug() << "学生开始学习"; }
|
二、发送信号
信号通过 emit 关键字发送:
需要注意:
emit 本质上是 一个空宏
- 即使不写
emit,代码也可以正常工作
例如:
依然可以触发信号。
不过在 Qt 开发中,建议保留 emit,因为它可以明显表达代码意图,使代码更具可读性。
三、连接信号与槽
信号与槽之间需要通过 connect() 建立连接:
1
| connect(sender, signal, receiver, slot);
|
连接建立后:
当信号发射时,Qt 会自动调用对应的槽函数。
需要注意 连接顺序问题:
如果先发射信号,再建立连接:
那么信号不会触发任何槽函数。
四、自定义信号槽示例
下面通过一个简单的示例说明自定义信号与槽的使用。
假设有这样一个场景:
老师说 “上课了”,学生开始学习。
1 定义 Teacher 类
1 2 3 4 5 6 7 8 9 10
| class Teacher : public QObject { Q_OBJECT
public: explicit Teacher(QObject *parent = nullptr);
signals: void classBegin(); };
|
2 定义 Student 类
1 2 3 4 5 6 7 8 9 10
| class Student : public QObject { Q_OBJECT
public: explicit Student(QObject *parent = nullptr);
public slots: void startStudy(); };
|
实现:
1 2 3 4
| void Student::startStudy() { qDebug() << "学生回到座位,开始学习"; }
|
3 在主程序中连接信号与槽
1 2 3 4 5
| Teacher teacher; Student student;
QObject::connect(&teacher, &Teacher::classBegin, &student, &Student::startStudy);
|
4 发送信号
1
| emit teacher.classBegin();
|
执行结果:
逻辑流程:
1 2 3
| Teacher 发出信号 ↓ Student 槽函数被调用
|
五、带参数的信号与槽
信号和槽可以携带参数,用于传递数据。
例如:
1 2
| signals: void classBegin(QString courseName);
|
对应槽:
1 2
| public slots: void startStudy(QString courseName);
|
连接:
1 2
| connect(&teacher, &Teacher::classBegin, &student, &Student::startStudy);
|
发送信号:
1
| emit teacher.classBegin("C++");
|
输出:
需要注意:
槽函数的参数数量可以 少于信号参数,但不能多于信号参数。
例如:
1 2 3 4
| Signal: void signal(int a, int b) Slot: void slot(int a) ✓ Slot: void slot(int a, int b) ✓ Slot: void slot(int a, int b, int c) ✗
|
六、自定义类通常继承 QObject
在 Qt 中,如果一个类需要使用信号槽机制,通常需要继承 QObject:
1 2 3 4
| class MyClass : public QObject { Q_OBJECT };
|
原因是:
- 信号槽依赖 Qt 的 元对象系统
QObject 提供对象树机制
- 支持动态属性、事件系统等
如果类没有继承 QObject,则无法使用信号与槽。
总结
Qt 的自定义信号与槽为应用程序提供了一种 灵活、解耦的通信机制。通过它可以轻松实现对象之间的事件通知,而不需要直接依赖彼此。
其核心流程可以总结为:
1 2 3 4
| 定义 Signal 定义 Slot connect 建立连接 emit 发射信号
|