Qt信号槽——传递自定义数据类型

本文介绍Qt中如何使用qRegisterMetaType注册自定义数据类型,并通过两种方法实现信号槽间的数据传递。第一种直接注册自定义类型;第二种利用QVariant类型转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Qt信号槽——传递自定义数据类型

Qt的信号槽机制支持传递int,short,double等C语言的基本类型的变量,也可以传递Qt自己的数据类型。但是在传递用户自己定义的数据类型,或其他数据类型是需要进行注册的。

原因:当一个signal被放到队列(queued)中时,它的参数(arguments)也会被一起放到队列中。这就意味着参数在被传送到slot之前不仅要被拷贝,存储在队列(queue)中。为了能够在队列中存储这些参数(argument),Qt需要去construct、destruct、copy这些对象,而为了让Qt知道怎么去做这些事情,参数的类型需要使用qRegisterMetaType来注册。

参考以下文档: (不得不说Qt的文档是真的好,基本所有问题都可以查看文档解决)

Qt文档:QMetaType Class | Qt Core 5.15.11

原文链接:Qt信号槽机制-传递自定义数据类型(qRegisterMetaType)_宁静致远2021的博客-优快云博客_qt信号槽传递自定义类型

原文链接:Qt信号槽传递自定义结构体_Liu-Eleven的博客-优快云博客_qt信号槽传递自定义结构体

1. 方法一

1.1 步骤

  1. 自定义一种类型,在这个类型所在文件包含头文件:#include <QMetaType>

  2. 在类型定义完成后,声明自定义类型:Q_DECLARE_METATYPE(Type)

 // This example shows a typical use case of Q_DECLARE_METATYPE():
 struct MyStruct
 {
     int i;
     ...
 };
 ​
 Q_DECLARE_METATYPE(MyStruct)

3.在main()函数中注册自定义类型:template <typename T> int qRegisterMetaType(const char *typeName);

 // This example registers the class MyClass:
 ​
 qRegisterMetaType<MyClass>("MyClass");

如果还希望使用这种类型的引用,还需要注册:qRegisterMetaType<MyClass>("MyClass&");

此方法可以直接在信号槽中使用自己定义的数据类型。

1.2 例程-传递自定义类

 // 1.包含头文件
 #include <QMetaType>     
 //必须包含QMetaType,否则会出现下面错误:     
 //error: expected constructor, destructor, or type conversion before ‘;’ token     
 #include <QString>
 ​
 // 2.自定义类型
 class MyClass
 {
 private:     
     int _count;     
     QString _text;
 public:     
     MyClass();     
     MyClass(int, QString);     
     int count();     
     QString text();       
 };
 ​
 // 3.声明自定义类型
 //在自定义类或者结构体等声明后紧接着用宏Q_DECLARE_METATYPE声明自定义自定义数据类型   
 Q_DECLARE_METATYPE(MyClass)
 
// 4.在main()函数中注册
 int main(int argc, char *argv[])
 {
     qRegisterMetaType<MyClasst>("MyClass");
     qRegisterMetaType<MyClasst>("MyClass&");
 ​
     QApplication a(argc, argv);
     Form w;
     w.show();
 ​
     return a.exec();
 }

2. 方法二

第二种方法是通过QVariant类型进行转换。

参考文档:QVariant Class | Qt Core 5.15.11

2.1 例程-传递自定义结构体

2.1.1 定义结构体并注册元对象

// 1.定义结构体
 typedef struct student
 {
     int     m_id;
     int     m_age;
     QString m_name;
 } student_t;
 ​
 // 2.注册元对象
 Q_DECLARE_METATYPE(student_t)
     
 class MainWindow : public QMainWindow
 {
     Q_OBJECT
         
 public:
     explicit MainWindow(QWidget *parent = 0);
     ~MainWindow();

 private slots:
     void slot_recvStudent(const QVariant varValue);
     
 signals:
     void signal_sendStudent(const  QVariant varValue);
 }

2.1.2 连接信号槽

connect(this, SIGNAL(slot_recvStudent(const QVariant)), this, SLOT(signal_sendStudent(const  QVariant)));

2.1.3 使用QVariant打包数据并发送

student_t iStudent;
 ​
 iStudent.m_id   = 1;
 iStudent.m_age  = 18;
 iStudent.m_name = "Box";
 ​
 QVariant varValue = QVariant::fromValue(iStudent);
 emit signal_sendStudent(varValue);

2.1.4 在槽函数中解包并打印

void MainWindow::slot_recvStudent(const QVariant varValue)
 {
     if (varVlaue.canConvert<student_t>())
     {
         student_t student = varValue.value<student_t>();
         qDebug() << "ID : " << student.m_id;
         qDebug() << "Age : " << student.m_age;
         qDebug() << "Name : " << student.m_name;
     }
 }

### Qt信号与槽机制的深入解析 #### 1. 信号与槽的基本概念 信号与槽是Qt框架的核心特性之一,用于对象之间的通信。当某个事件发生时,发送者会发出一个信号,接收方则通过槽函数来响应这个信号[^1]。 - **信号 (Signals)** 信号是在特定条件下自动发射的消息通知。它们通常由某些用户操作或程序逻辑触发。信号可以在类中使用 `signals:` 关键字声明,并且不需要编写具体的实现代码,因为这些代码是由元对象编译器(Meta Object Compiler, moc)自动生成的[^3]。 - **槽 (Slots)** 槽是一些普通的成员函数,可以被信号激活并执行相应的动作。槽可以通过标准的方式定义,在类中使用 `slots` 或 `public slots` 进行标记[^4]。 --- #### 2. 信号与槽的连接方式 信号和槽之间需要建立联系才能正常工作,这通常是通过 `connect()` 函数完成的。 ##### (1)传统宏语法 传统的信号与槽连接方式依赖于 `SIGNAL()` 和 `SLOT()` 宏: ```cpp QObject::connect(sender, SIGNAL(signalName()), receiver, SLOT(slotName())); ``` ##### (2)现代语法 从Qt 5开始引入了更安全、直观的新语法,支持直接传递函数指针或 lambda 表达式: ```cpp QObject::connect(sender, &SenderClass::signalName, receiver, &ReceiverClass::slotName); ``` 这种新语法不仅提高了可读性,还能够在编译期捕获错误,从而减少运行时异常的发生。 --- #### 3. 参数传递 信号和槽可以携带参数进行数据交换。需要注意的是,槽函数的签名应兼容信号的签名——即槽函数能够接受信号所传递的所有参数或者部分参数。 如果希望更加灵活地处理复杂的业务场景,则可以选择利用 Lambda 表达式作为中间桥梁来进行转换或过滤操作: ```cpp QObject::connect(button, &QPushButton::clicked, [=]() { qDebug() << "Button clicked!"; }); ``` 上述例子展示了如何绑定按钮点击事件到匿名函数上,实现了无需显式创建额外的方法即可快速响应的需求。 --- #### 4. 实现细节剖析 为了理解其实现原理,可以从以下几个方面入手: - **MOC工具的作用** 当我们定义了一个继承自 QObject 的子类时,若该类包含了 signal 或 slot 成员,则需经过 MOC 处理生成对应的 C++ 文件。此过程主要包括两部分内容:一是扩展原生类型接口;二是构建内部消息队列管理结构以便后续分发调用链路[^2]。 - **QMetaObject反射能力** Qt 利用了 QMetaObject 提供的强大反射功能来动态获取类的信息以及匹配合适的回调入口点。每当一个新的 connection 被注册成功后都会记录下来形成一张映射表,这样即使目标实例尚未存在也可以提前预定好未来的互动关系。 --- #### 5. 示例代码展示 下面给出一段简单的演示代码说明基本用法: ```cpp class MyClass : public QObject { Q_OBJECT public: explicit MyClass(QObject *parent = nullptr); signals: void mySignal(int value); // 自定义带参信号 public slots: void mySlot(const QString& text); // 自定义槽函数 }; // 构造函数初始化 MyClass::MyClass(QObject *parent) : QObject(parent) {} int main(int argc, char *argv[]) { QApplication app(argc, argv); MyClass obj; // 使用lambda表达式捕捉外部变量 int counter = 0; QObject::connect(&obj, &MyClass::mySignal, [&counter](int val){ ++counter; std::cout << "Received Signal with Value:" << val << ", Counter now is:" << counter << std::endl; }); // 手动触发信号 emit obj.mySignal(42); return app.exec(); } ``` --- ### 总结 通过对以上内容的学习可以看出,Qt 中的信号与槽是一种非常优雅的设计模式,极大地简化了 GUI 应用开发过程中组件间松耦合式的交互需求。它背后依托着成熟的元编程技术和完善的基础设施支撑才得以高效运转起来[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值