1. 信号 和 槽
Qt 信号与槽机制 是一种用于对象间通信的低耦合设计模式,核心思想是:当某个信号触发,自动调用预先关联的处理函数(槽函数)。
在 Qt 中,如果一个类需要使用信号与槽机制,则该类必须继承自 QObject ,且必须在类的私有部分声明 Q_OBJECT
宏。
Q_OBJECT 宏 通常位于类定义的顶部。
class MyClass : public QObject // 必须继承自 QObject
{Q_OBJECT // 必须在私有部分声明
};
2. 信号 Signal
通俗意义上,信号
是,在特定事件发生时,发出的通知;其本质是函数声明 —— 通过 signals
关键词声明,没有函数体。
signals:void MySignal(QString text);
特点:
- 信号的返回值类型必须是 void 。
- 信号可以携带参数(或者不带),这些参数会被传递给槽函数。
- 动态绑定 —— 信号与槽的连接在运行时建立。
在 Qt 中,自定义信号建议使用 emit
关键词来显式触发(尽管 emit 是可选的)。
emit MySignal("test");
3. 槽 Slot
槽
是,响应信号的普通函数。槽函数通过 slots
关键词声明(Qt5 后可以省略),本质上是类的成员函数,可以使用 public 、protected 、private 等访问控制符修饰(通常将槽设为 public 或 protected)。
public slots:void MySlot1(int value);
public: // Qt5 版本后可以省略 slotsvoid MySlot2(int value);
槽函数可以像普通函数一样直接调用,无需通过信号触发,且槽函数支持重载(多个同名但参数不同的槽函数)。
4. connect
connect 函数的功能是,将 信号 与 槽 进行绑定,形成响应关系。
在 Qt 发展过程中,connect
函数经历了多次改进,主要目的是提高类型安全性和代码可维护性。
4.1 Qt4 及之前版本
在 Qt4 及之前版本中,使用字符串宏 SIGNAL()
和 SLOT()
传递信号和槽。
connect(button, SIGNAL(clicked()), this, SLOT(MyClick()));
// button 为 QPushButton*
// 第二个参数应传递 SIGNAL(clicked()) 而不是 SIGNAL(QPushButon::clicked())
4.2 Qt5+ 版本
connect(button, &QPushButton::clicked, this, &Widget::MyClick);
关键改进:
- 支持编译时检查:信号和槽的参数类型在编译时检查,避免运行时错误。
- 支持槽函数重载,通过使用 static_cast 来帮助编译器选择正确的重载版本。
public slots:void MyClick(); // 第一个重载版本void MyClick(bool checked); // 第二个重载版本
---
// 连接到带参数的 MyClick 槽函数
connect(button, &QPushButton::clicked, this, static_cast<void(Widget::*)(bool)>(&Widget::MyClick));
- 支持直接使用成员函数指针,如 &QPushButton::clicked 。
- 支持 lambda 表达式。
在 Qt 的信号槽中,lambda 的捕捉列表推荐使用传值
[=]
,避免捕获的对象在 lambda 表达式执行时已销毁导致未定义行为。
4.3 disconnect
disconnect 的功能是,断开 信号 与 槽 的绑定关系。
“自动连接清理”机制:
当信号发送者 sender 或接收者 receiver 被销毁时,Qt 会自动断开所有相关的信号与槽的连接(一个信号可以连接多个槽,一个槽可以被多个信号所连接),所以通常无需手动 disconnect 。
disconnect(button, &QPushButton::clicked, this, &Widget::MyClick);
5. 继承机制与参数匹配规则
控件会继承其父控件的所有信号、槽和方法,这是面向对象编程中继承机制的核心特性之一。
对于一个控件,可以通过查看 Qt 文档、Inherits
(继承自)逐层追溯父类,了解一个控件的所有可用信号和功能等。
5.1 信号和槽继承的原理
信号 和 槽 的本质都是函数,子类会自动继承父类的所有信号和槽(包括直接父类和间接父类)。
5.2 如何逐层查看信号和槽?
通过官方文档中的 Inherits 链 —— Qt 官方文档为每个类提供了完整的继承链信息。
-
QPushButton 自己的信号:文章显示 QPushButton 没有新增信号,主要依赖于父类 QAbstractButton 。
-
QAbstractButton 的信号:通过查阅文档,发现
void clicked(bool checked = false);void pressed();void released();void toggled(bool checked);
-
… …
5.3 信号和槽的参数匹配规则
-
信号的参数数量 >= 槽的参数数量;
-
信号和槽的类型必须兼容(允许隐式类型转换)。
signals:void MySignal(QString text1, QString text2);
public slots:void MyHandle(QString text);connect(this, &Widget::MySignal, this, &Widget::MyHandle);emit MySignal("test1", "test2");
当 信号的参数数量 多于 槽函数的参数 时,槽函数会按顺序接收 信号 的前 N 个参数(N 为槽函数的参数数量),多余的信号参数被自动忽略。