QT信号与槽机制
connect 连接
QObject::connect()是实现信号与槽机制的核心函数,用于建立对象间的通信。
1. 基本语法
- Qt5 + 推荐语法(类型安全)
connect(sender, &SenderClass::signalName, receiver, &ReceiverClass::slotName, connectionType);
sender: 发送者SenderClass::signalName: 发送信号名称receiver: 接收者ReceiverClass::slotName:接收后调用的槽函数名称connectionType:连接类型
2. 连接类型
Qt::AutoConnection(默认)
自动判断:同线程使用DirectConnection,跨线程使用QueuedConnection。Qt::DirectConnection
槽函数在信号发送线程中立即执行(类似函数调用)。Qt::QueuedConnection
槽函数在接收者线程的事件循环中异步执行(跨线程安全)。Qt::BlockingQueuedConnection
类似QueuedConnection,但阻塞发送者线程直到槽执行完毕(慎用,需防死锁)。Qt::UniqueConnection
避免重复连接(与上述类型按位或组合使用
3. 信号与槽要求
- 信号和槽声明:
在类中使用signals/slots关键字声明,或使用Q_SIGNALS/Q_SLOTS。 - 元对象系统:
类必须继承QObject并在类定义开头添加Q_OBJECT宏(用于元对象编译 MOC)
4. 连接方式扩展
- 连接重载信号
使用静态转型或 qOverload<>(C++14):
由于信号可以有多个重载(例如,QComboBox::currentIndexChanged 有两个重载:一个带有 int 参数,另一个带有 const QString & 参数 ),因此需要明确指定要连接的是哪一个版本。
void QComboBox::currentIndexChanged(int index); // 版本1:带整型参数
void QComboBox::currentIndexChanged(const QString& text); // 版本2:带字符串参数
当使用 Qt5 的新型语法(基于函数指针)进行信号槽连接时:
// 处理重载信号
connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot);
编译器无法自动确定要使用哪个重载版本,因此需要用 QOverload 明确指出目标重载。
QOverload<int>::of 详解
1. 语法结构
verload<参数类型>::of(成员函数指针)
2. 在连接中的使用
// 明确选择 currentIndexChanged(int) 重载版本
QOverload<int>::of(&QComboBox::currentIndexChanged)
3. 从 Qt5 到 Qt6 的变化
| 版本 | 解决方案 | 示例 |
|---|---|---|
| Qt5 (5.0-5.6) | 显式类型转换 | static_cast<void (QComboBox:😗)(int)>(&QComboBox::currentIndexChanged) |
| Qt5 (>=5.7) | QOverload |
QOverload |
| Qt6 | 简化的 qOverload |
qOverload |
- Lambda 表达式作为槽
connect(button, &QPushButton::clicked, [=]() {
qDebug() << "Button clicked!";
});
注意:Lambda 中捕获局部变量时,需确保生命周期(跨线程时谨慎捕获)。
- 信号连接信号
connect(srcObj, &SrcClass::signalA, destObj, &DestClass::signalB);
5. 断开连接
- 手动断开
disconnect(sender, nullptr, receiver, nullptr); // 断开 sender 与 receiver 所有连接
disconnect(connectionHandle); // 通过 QMetaObject::Connection 断开
- 自动断开
当sender或receiver被销毁时,连接自动断开。
6. 连接有效性检查
- 返回值:
connect()返回QMetaObject::Connection对象,用于管理连接状态。 - 检查连接:
QMetaObject::Connection conn = connect(...);
if (conn) { /* 连接成功 */ }
7. 多线程注意事项
- 跨线程通信:
必须使用QueuedConnection或BlockingQueuedConnection。 - 线程亲和性:
通过QObject::thread()和moveToThread()管理对象所属线程。 - 资源竞争:
QueuedConnection 传递的参数必须是元类型(使用qRegisterMetaType()注册复杂类型)。
8. Qt 传递自定义类型
在QT中使用槽与信号传递自定义类型,需要先进行类型注册。
方式1:使用 qRegisterMetaType<T>() 注册自定义类型
- 作用:在运行时将类型的元信息注册到Qt的类型系统中。使得
Type可用于跨线程的信号槽通信。 - 阶段:运行时 (Run-time),通常在 main 函数或初始化代码中调用。
- QVariant: 非必需,但建议一起使用。QVariant 的某些操作(如跨线程转换)可能需要它
- 信号槽(自动连接):必需。用于排队连接 (QueuedConnection),这是跨线程通信的默认方式。
信号槽(自动连接):不足以支持跨线程,同线程的直连(DirectConnection)可以,因为它不涉及拷贝。
方式2:在类声明中使用 Q_DECLARE_METATYPE(Type) 声明自定义类型
- 作用:让编译器在编译时生成类型的元信息,使得
Type可用于QVariant,QSettings等Qt的跨线程通信。 - 阶段:编译时(Compiler Time)
- QVariant: 必需。没有它,QVariant无法识别或存储自定义类型。
- 信号槽(自动连接):不足以支持跨线程,同线程的直连(DirectConnection)可以,因为它不涉及拷贝。
总结
- 两者关系:它们是互补的,而不是二选一。对于需要跨线程传递的自定义类型,两者通常都需要。
- 正确流程:
- 在头文件中声明:Q_DECLARE_METATYPE(MyCustomType)
- 在源文件中注册(通常在 main 函数或某个初始化函数中):qRegisterMetaType
("MyCustomType"); - 如果类型在命名空间中:需要在外层声明和注册,例如 Q_DECLARE_METATYPE(MyNamespace::MyCustomType)
#include <QMetaType> // 必须包含这个头文件
// 自定义数据类型
class MyDataType
{
//省略成员变量和方法...
};
//在头文件中声明
Q_DECLARE_METATYPE(MyCustomType);
// main 函数或某个初始化函数中
// 关键步骤:在运行时注册自定义类型
// 这是跨线程信号槽传递所必需的
qRegisterMetaType<MyDataType>("MyDataType");
qRegisterMetaType<QSharedPointer<MyDataType>>("QSharedPointer<MyDataType>");
// 如果需要,也可以注册自定义类型的指针版本
// qRegisterMetaType<MyDataType*>("MyDataType*");
配合智能指针使用
对于Qt跨线程通信,推荐使用一下智能指针:
QSharedPointer<T>:用于跨线程传递复杂数据,其内部维护一个引用计数,当引用计数为0时自动释放内存。QWeakPointer<T>:用于跨线程传递复杂数据,其内部维护一个弱引用,当引用对象被销毁时自动失效(配合QSharedPointer使用)std::shared_ptr: C++11 标准智能指针,也需要注册才能使用
#include <QString>
#include <QMetaType>
#include <QSharedPointer>
#include <memory>
// 前置声明
class SmartData;
// 定义智能指针类型别名 一般使用智能指针都是要起别名的,方便使用
using SmartDataPtr = QSharedPointer<SmartData>;
using SmartDataStdPtr = std::shared_ptr<SmartData>;
// 自定义数据类型
class SmartData
{
public:
SmartData();
SmartData(int id, const QString& name, double value);
// 不需要显式定义拷贝构造函数和赋值运算符
// 因为使用智能指针,不会直接拷贝对象
QString toString() const;
int id() const;
QString name() const;
double value() const;
void setValue(double value);
private:
int m_id;
QString m_name;
double m_value;
};
// 声明元类型 - 编译时声明
// 1. 声明原始类型(可选,如果需要在QVariant中使用)
Q_DECLARE_METATYPE(SmartData)
// 2. 声明智能指针类型(必须)
Q_DECLARE_METATYPE(SmartDataPtr)
Q_DECLARE_METATYPE(SmartDataStdPtr)
// 在主函数中
// 关键步骤:在运行时注册自定义类型和智能指针类型
// 1. 注册原始类型(如果需要)
qRegisterMetaType<SmartData>("SmartData");
// 2. 注册智能指针类型(必须)
qRegisterMetaType<SmartDataPtr>("SmartDataPtr");
qRegisterMetaType<SmartDataStdPtr>("SmartDataStdPtr");
// 3. 也可以使用模板方式注册(推荐)
qRegisterMetaType<QSharedPointer<SmartData>>();
qRegisterMetaType<std::shared_ptr<SmartData>>();
SmartController controller;
controller.startDemo();

浙公网安备 33010602011771号