CQt-槽函数收不到信号问题信号的注册
[C++&Qt] 槽函数收不到信号问题(信号的注册)
- 📢博客主页:
- 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
- 📢本文由 丶布布 原创,首发于 CSDN, 转载注明出处 🙉
- 📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨
一. 需要注册信号参数的情况
1、跨线程的信号槽连接(使用 QueuedConnection)
当信号和槽位于不同线程,且连接方式为
Qt::QueuedConnection
或
Qt::BlockingQueuedConnection
时,
参数类型必须注册
。
原因:跨线程通信时,Qt 需要将参数序列化到接收线程的事件队列中,这要求类型必须能被 Qt 的元对象系统识别。
2、使用 QVariant 传递自定义类型
如果信号参数是
自定义类型
,且需要与
QVariant
结合使用,必须注册类型。
二. 不注册可能引发的问题
1、运行时警告或错误
如果未注册自定义类型,
Qt
会在运行时输出类似以下警告:
QObject::connect: Cannot queue arguments of type 'MyCustomType'
(Make sure 'MyCustomType' is registered using qRegisterMetaType().)
后果 :跨线程的信号槽调用会失败,槽函数不会执行,程序可能无响应或崩溃。
2、参数无法正确传递
未注册的类型无法被 Qt 序列化/反序列化,导致槽函数接收到的参数是无效或未初始化的值。
3、无法与 QVariant 交互
自定义类型无法通过
QVariant
存储或传递,导致相关功能(如属性系统、模型/视图)失效。
三. 如何注册自定义类型
1、使用 Q_DECLARE_METATYPE 宏
#include <QMetaType>
// 自定义类型定义
struct MyCustomType {
int id;
QString name;
};
// 声明元类型支持(放在头文件末尾)
Q_DECLARE_METATYPE(MyCustomType)
注 : Q_DECLARE_METATYPE 的作用
1)编译时元信息生成
Q_DECLARE_METATYPE 宏会为类型生成编译时的元信息(如类型名称、大小、对齐方式等),使得以下功能可用:
QVariant
的构造和类型转换(例如QVariant::fromValue 和 QVariant::value
)。- 类型在模板和宏中的静态识别(例如
QMetaType
的静态接口)。
2)隐式要求
如果未使用
Q_DECLARE_METATYPE
,即使通过
qRegisterMetaType
注册了类型,以下操作可能失败:
MyCustomType data;
QVariant variant = QVariant::fromValue(data); // 编译错误!
2、使用 qRegisterMetaType 注册类型
在程序启动时(如 main 函数、构造函数等中)注册类型:
#include <QMetaType>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 注册自定义类型
qRegisterMetaType<MyCustomType>("MyCustomType");
// 如果类型有默认构造函数,可以简写为:
qRegisterMetaType<MyCustomType>();
return app.exec();
}
四. 示例:跨线程信号槽的正确用法
// 自定义类型
struct MyCustomType {
int id;
QString name;
};
Q_DECLARE_METATYPE(MyCustomType)
// 发送者类
class Sender : public QObject {
Q_OBJECT
public:
void sendData() {
MyCustomType data{1, "Test"};
emit signalData(data); // 发送信号
}
signals:
void signalData(const MyCustomType& data);
};
// 接收者类
class Receiver : public QObject {
Q_OBJECT
public slots:
void onDataReceived(const MyCustomType& data) {
qDebug() << "Received:" << data.id << data.name;
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
qRegisterMetaType<MyCustomType>(); // 注册类型
Sender sender;
Receiver receiver;
QThread thread;
// 跨线程连接
QObject::connect(&sender, &Sender::signalData,
&receiver, &Receiver::onDataReceived,
Qt::QueuedConnection);
//将接受者移至线程中,这样与发送者即分属于不同的线程中
receiver.moveToThread(&thread);
thread.start();
sender.sendData();
return app.exec();
}
五. 为什么“仅用 qRegisterMetaType 也能工作”?
场景 1 :跨线程信号槽通信
如果仅在跨线程信号槽中使用自定义类型,且未直接操作 QVariant,程序可能正常执行。
原因 :
qRegisterMetaType
在运行时注册了类型,使得 Qt 能正确序列化参数。Q_DECLARE_METATYPE
的缺失在此场景下可能不会立即暴露问题。
场景 2 :低版本 Qt 的宽松处理
某些旧版 Qt(如 Qt4)对类型注册的要求较为宽松,可能允许未声明 Q_DECLARE_METATYPE。
风险:
这种行为是未定义的,可能因 Qt 版本或平台不同而失效。
下雨天,最惬意的事莫过于躺在床上静静听雨,雨中入眠,连梦里也长出青苔。 |