目录

CQt-槽函数收不到信号问题信号的注册

[C++&Qt] 槽函数收不到信号问题(信号的注册)

  • 📢博客主页:
  • 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢本文由 丶布布 原创,首发于 CSDN, 转载注明出处 🙉
  • 📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨


一. 需要注册信号参数的情况

1、跨线程的信号槽连接(使用 QueuedConnection)

当信号和槽位于不同线程,且连接方式为 Qt::QueuedConnectionQt::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 版本或平台不同而失效。


下雨天,最惬意的事莫过于躺在床上静静听雨,雨中入眠,连梦里也长出青苔。