Qt的信号槽机制学习一

一、Qt理论知识简记

(一)信号与槽[1]

        信号与槽是Qt编程的基础,其使得处理界面上各个组件的交互操作变得比较直观和简单,GUI(Graphical User Interface)程序设计的主要工作就是对界面上各组件的信号进行相应。信号(signal)是在特定情况下被发射的通知;槽(slot)是对信号进行响应的函数,槽函数与一般的C++函数一样,可以具有任何参数,也可以被直接调用。

        信号与槽关联是基于函数QObject::connect()实现的,其可以通过SIGNAL/SLOT(Qt的宏)表现,也可以通过函数指针表现,具体示例如下:

# SIGNAL/SLOT(Qt宏)形式
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
一般用法如下:
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));# 函数指针形式(信号名称唯一,不存在参数不同的其它同名信号)
QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection);
一般用法示例如下:
connect(sender, &sender::signal, receiver, &receiver::slot);

        在使用信号与槽时,需要注意的规则:

1. 在使用信号与槽的类中,必须在类的定义中插入宏Q_OBJECT;

2. 当信号和槽函数带有参数时,在函数connect()里要指明各参数的类型,但不用指明参数名称;

3. 当一个信号与多个槽函数关联时,槽函数按照建立连接时的顺序依次进行;

4. 多个信号可以连接同一个槽函数;

5. 一个信号可以连接另一个信号;

6. 当一个信号被发射时,与其关联的槽函数通常被立即执行。只有当信号关联的所有槽函数运行完毕后,才运行发射信号处后面的代码

(二)元对象系统[1][4][5]

        Qt中引入了元对象系统(Meta-Object System)对标准C++语言进行扩展,增加了信号与槽、属性系统、动态翻译等特性,为编写GUI应用程序提供了极大的方便。Qt的元对象系统的功能建立在以下三个方面:

1. QObject类是所有使用元对象系统的类的基类;

2. 必须在一个类的开头部分插入宏Q_OBJECT,这样这个类才可以使用元对象系统的特性;

3. 在构建项目时,MOC会读取C++源文件,当它发现类的定义里有Q_OBJECT宏时,它就会为这个类生成另一个包含元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被标准C++编译器编译和链接(MOC为每个QObject的子类提供必要的代码来实现元对象系统的特性)。

       元对象系统的特性是通过QObject的一些函数来实现的。元对象系统的特性包括元对象(meta-Object)、类型信息、动态翻译、对象树(Object-Tree)、信号与槽和属性系统(类的定义代码中需要用宏Q_PROPERTY定义属性)。

        由于C++的RTTI机制只能提供有限的类型信息,于是Qt构建了自己的元对象系统,并基于元对象信息实现了强大的信号槽机制[4]

简略补充说明(详细可见参考资料[1]-P48): 

        对象树。使用QObject及其子类创建的对象(统称为QObject对象)是以对象树的形式来组织的。创建一个QObject对象时若设置一个父对象,它就会被添加到父对象的子对象列表里。一个父对象被删除时,其全部子对象就会被自动删除。QObject类的构造函数里有一个参数parent,用于设置对象的父对象。

        元对象。每个QObject及其子类的实例都有一个自动创建的元对象,元对象是QMetaObject类型的实例。元对象存储了类的实例所属类的各种元数据,包括类信息元数据、方法元数据、属性元数据等。所以,元对象实质上是对类的描述

        通过使用QObject和QMetaObject提供的接口函数,可以在运行时获得一个对象的类名称以及其父类的名称,判断其是否从某个类继承而来。由于这个元对象系统的特性,实现运行时类型信息获取并不需要C++编译器的运行时类型信息(run-time type information,RTTI)支持。

二、Qt信号槽机制下的信号与信号槽的连接方式

        枚举类型Qt::ConnectionType包含四个值,分别为Qt::AutoConnection(默认值)Qt::DirectConnectionQt::QueuedConnectionQt::BlockingQueuedConnection

        Qt::AutoConnection(默认值),如果信号的接收者与发射者在同一个线程中,就使用Qt::DirectConnection方式,否则使用Qt::QueuedConnection方式,在信号发射时自动确定关联方式;

        Qt::DirectConnection,信号被发射时槽函数立即运行,槽函数与信号在同一个线程中;

        Qt::QueuedConnection,在事件循环回到接收者线程后运行槽函数,槽函数与信号在不同的线程中;

        Qt::BlockingQueuedConnection,与Qt::QueuedConnection相似,区别是信号线程会阻塞,直到槽函数运行完毕。当信号与槽函数在同一个线程中时绝对不能使用这种方式,否则会造成死锁。

图1 QT信号槽机制下的信号与信号槽的连接方式

三、带参信号传递与带参槽函数接收

(一)信号与槽位于同一个线程

        1、参数为Qt元对象系统中的基础类型

// mainwindow.h#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void emitQString(QString,int);public slots:void showText(QString,int);private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H// mainwindow.cpp#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(ui->pushButton,&QPushButton::clicked,this, [=](){emit emitQString("My age is",18);});connect(this,SIGNAL(emitQString(QString,int)),this,SLOT(showText(QString, int)));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::showText(QString, int)
{int age = 18;ui->label->setText("My age is"+QString::number(age));
}// main.cpp#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
图2 信号与槽位于相同线程的Qt基础类型参数传递实验结果

        2、参数为自定义类型(Qt元对象系统不能自然识别)

// mainwindows.h#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEstruct MyStruct
{MyStruct::MyStruct(QString aa,int bb,int *cc):a(aa),b(bb),c(cc){};QString a;int b;int *c;
};class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void emitQString(MyStruct);public slots:void showText(MyStruct);private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H// mainwindow.cpp#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(ui->pushButton,&QPushButton::clicked,this, [=](){int *a = new int(12);MyStruct s = MyStruct("My age is",6,a);emit emitQString(s);delete a;});connect(this,SIGNAL(emitQString(MyStruct)),this,SLOT(showText(MyStruct)));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::showText(MyStruct s)
{int age = s.b + *(s.c);ui->label->setText(s.a+QString::number(age));
}// main.cpp#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
图3 信号与槽位于相同线程的自定义结构体类型参数传递实验结果

(二)信号与槽位于不同线程[2]

        1、参数为Qt元对象系统中的基础类型

// MyThread_1.h#ifndef MYTHREAD_1_H
#define MYTHREAD_1_H#include <QThread>class MyThread_1:public QThread
{Q_OBJECTpublic:explicit MyThread_1(QObject *parent = nullptr);protected:void run();signals:void emitString(QString, int);
};#endif // MYTHREAD_1_H// MyThread_1.cpp#include "MyThread_1.h"MyThread_1::MyThread_1(QObject *parent) : QThread(parent)
{}void MyThread_1::run()
{int age = 18;QString a = "My age is";emit emitString(a,age);
}// mainwindow.h#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:public slots:void showText(QString,int);private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H// mainwindow.cpp#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "MyThread_0.h"
#include "MyThread_1.h"
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);MyThread_1* subThread = new MyThread_1;connect(ui->pushButton_2,&QPushButton::clicked, this, [=](){subThread->start();});connect(subThread,SIGNAL(emitString(QString,int)),this,SLOT(showText(QString,int)));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::showText(QString text,int age)
{ui->label_2->setText(text+QString::number(age));
}// main.cpp#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
图4 信号与槽位于不同线程的Qt基础类型参数传递实验结果

        2、参数为自定义类型(Qt元对象系统不能自然识别)

        2.1 未注册元对象类型直接使用

        由图5可知,自定义结构体类型无法直接作为信号参数传递到位于不同线程中的槽函数中,不同线程间的信号和槽通过Qt::QueuedConnection类型连接。程序报错为“QObject::connect: Cannot queue arguments of type 'MyStruct'(Make sure 'MyStruct' is registered using qRegisterMetaType().)”。

图5 信号与槽位于不同线程的自定义结构体类型参数传递实验结果

        2.2 注册元对象类型使用

        在注册元对象类型时,首先需要在自定义结构体定义后接上“Q_DECLARE_METATYPE(自定义结构体名称);”,然后在该信号connect前加入“int id = qRegisterMetaType<自定义结构体名称>();”以完成元对象类型注册。

        需要注意的是,自定义结构体中需要有合适的默认构造函数可用。一般而言不重新定义构造函数,即使用默认构造函数。

图6  Qt Assistant示例(源自参考资料[2])
// MyThread_1.h#ifndef MYTHREAD_1_H
#define MYTHREAD_1_H#include <QThread>struct MyStruct
{QString a;int b;int *c;
};
Q_DECLARE_METATYPE(MyStruct);class MyThread_1:public QThread
{Q_OBJECTpublic:explicit MyThread_1(QObject *parent = nullptr);protected:void run();signals:void emitString(MyStruct);
};#endif // MYTHREAD_1_H// MyThread_1.cpp#include "MyThread_1.h"MyThread_1::MyThread_1(QObject *parent) : QThread(parent)
{}void MyThread_1::run()
{int *a = new int(12);MyStruct s;s.a = "my age is";s.b = 6;s.c = a;emit emitString(s);delete a;
}// mainwindow.h#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <MyThread_1.h>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:public slots:void showText(MyStruct s);private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H// mainwindow.cpp#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "MyThread_1.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);MyThread_1* subThread = new MyThread_1;connect(ui->pushButton_2,&QPushButton::clicked, this, [=](){subThread->start();});int id = qRegisterMetaType<MyStruct>();connect(subThread,SIGNAL(emitString(MyStruct)),this,SLOT(showText(MyStruct)));
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::showText(MyStruct s)
{int age = s.b + *(s.c);ui->label_2->setText(s.a+QString::number(age));
}// main.cpp#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
图7 信号与槽位于不同线程的自定义结构体类型参数传递实验结果

参考资料:

[1] Qt 6 C++开发指南 / 王维波,栗宝鹃,侯春望著.—北京:人民邮电出版社,2023.1.

[2] Assistant 5.15.2 (MSVC 2019 64-bit)-Qt::ConnectionType(Qt使用文档助手)

[3] 《QT从基础到进阶·三》信号槽关联类型Connection,DirectConnection,QueuedConnection,BlockingQueuedConnection-阿里云开发者社区 (aliyun.com)

[4] Qt中的元对象系统(Meta-Object System) - 知乎 (zhihu.com)

[5] 【Qt 元对象系统04】 深入浅出Qt的QMetaObject:探索元对象的魔法-阿里云开发者社区 (aliyun.com)

[6] 元对象系统 |Qt 核心 6.8.0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/57331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序员的相亲囧途:三万相亲费,能否换回真爱?

在快节奏的都市生活中&#xff0c;相亲已成为不少单身男女寻找另一半的重要途径。然而&#xff0c;宁波的唐先生却在这条路上遭遇了不小的挫折。28岁的他&#xff0c;身高1米78&#xff0c;本应是相亲市场上的“香饽饽”&#xff0c;却在“我主良缘”交了三万块钱相亲费后&…

【Android】使用TextView实现按钮开关代替Switch开关

介绍 Android 本身自己带的有开关控件&#xff0c;但是很多时候我们是不愿意使用这种开关的&#xff0c;感觉使用起来比较麻烦&#xff0c;特别是遇到需要延迟操作的情况。 比如有一个需求是这样的&#xff1a;我们需要打开一个设置&#xff0c;但是这个设置是否打开需要经过…

AI自媒体变现路径大盘点!建议收藏!

当下的我做为一人公司或者超级个体为目标的创业模式&#xff0c;无论是在写作、图文和短视频输出方面&#xff0c;我都是运用了N个AI工具来提升我的生产力。 这种创业模式就是一个人N个AI的模式&#xff0c;我们可以通过AI工具做提效来赚取差价&#xff0c;以时间复利来累计财…

Python的协程与传统的线程相比,是否能更有效地利用计算资源?在多大程度上,这种效率是可测量的?如何量化Python协程的优势|协程|线程|性能优化

目录 1. 协程与线程的基本概念 1.1 线程 1.2 协程 2. 协程的实现原理 2.1 基本示例 3. 协程与线程的效率对比 3.1 资源利用率 3.2 性能测试 4. 使用场景分析 4.1 适用场景 4.2 不适用场景 5. 性能监测与测量 5.1 使用时间记录 5.2 使用第三方库 6. 总结与展望 P…

服务器文件访问协议

服务器文件访问协议 摘要NFS、CIFS、SMB概述SMBWindows SMBLinux SambaPython SMB NFS 摘要 本篇博客参考网上文档和博客&#xff0c;对基于网络的服务器/主机的文件访问、共享协议进行简要总结&#xff0c;完整内容将会不断更新&#xff0c;以便加深理解和记忆 NFS、CIFS、S…

docker占用磁盘过多问题

我在windows系统上用docker&#xff0c;安装在C盘环境下&#xff0c;我发现C盘占用了大量的空间&#xff0c;查找后发现是docker的映像文件占用的&#xff0c;于是开始清理&#xff0c;中间还踩个坑&#xff0c;记录一下&#xff0c;下次需要的时候方便找。 踩坑 我本想移动映…

HarmonyOS:@Watch装饰器:状态变量更改通知

Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变&#xff0c;可以使用Watch为状态变量设置回调函数。 说明 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 从API version 11开始&#xff0c;该装饰器支持在元服务中使用。 一、概…

【Qt】控件——布局管理器、常见的布局管理器、布局管理器的使用、垂直布局、水平布局、网格布局、表单布局、Spacer

文章目录 Qt布局管理器垂直布局水平布局网格布局表单布局Spacer Qt 布局管理器 之前在使用 Qt 创建界面上的控件时&#xff0c;大多是通过 “绝对定位” 的方式来设定的。即每个控件所在的位置都需要计算坐标&#xff0c;最终通过 setGeometry 或者 move 方式进行摆放。 然而&a…

配置和排查 Lombok 在 IDEA 中使用的详细步骤

在日常开发中&#xff0c;Java 代码常常需要大量的样板代码&#xff0c;比如 getter、setter、toString 等方法。Lombok 是一个 Java 库&#xff0c;可以通过注解的方式&#xff0c;自动生成这些常见的代码&#xff0c;从而让代码更加简洁、清晰。比如&#xff0c;我们可以通过…

快速在win11上利用hyper-v安装虚拟系统:遭遇Start PXE over ipv4

以前习惯使用VMware&#xff0c;但在win11上折腾了很久都遇到各种麻烦&#xff0c;索性就上微软自家的Hyper-V&#xff0c;。作为微软自家的产品&#xff0c;Hyper-V 与 Windows 11 操作系统紧密结合&#xff0c;具有良好的兼容性和稳定性。在安装和使用过程中&#xff0c;与系…

Python中的数据可视化:Matplotlib基础与高级技巧

Python中的数据可视化&#xff1a;Matplotlib基础与高级技巧 数据可视化是数据分析和数据科学中不可或缺的一部分。通过图表&#xff0c;我们可以更直观地观察数据的分布和趋势。Matplotlib作为Python最基础、也是最广泛使用的绘图库之一&#xff0c;不仅支持多种常用图表&…

沈阳乐晟睿浩科技有限公司抖音小店新篇章

在当今数字化时代&#xff0c;电商行业如雨后春笋般迅速崛起&#xff0c;其中抖音小店凭借其庞大的用户基础、精准的推荐算法和便捷的购物体验&#xff0c;成为了电商领域的新宠。在这场电商变革中&#xff0c;沈阳乐晟睿浩科技有限公司&#xff08;以下简称“乐晟睿浩”&#…

1,国产FPGA(紫光同创)-IP核-PLL

本文默认在0&#xff0c;国产FPGA&#xff08;紫光同创&#xff09;-新建PDS工程基础上完成。 1&#xff0c;添加IP核 右击&#xff08;1&#xff09;空白处进行添加&#xff0c;点击New IP&#xff08;2&#xff09;进行新建IP核。 选择本次实验要配置的IP核-PLL&#xff08;…

“智能二维码”实现光伏行业数字信息化管理

近日&#xff0c;为了提升管理效率&#xff0c;国电投建业光伏电站将二维码引入设备巡视和班组建设中。 首先&#xff0c;使用传统纸质巡视作业卡&#xff0c;巡视工作强度大&#xff0c;容易出现错误&#xff1b;此外&#xff0c;“三会一活动”和培训记录等班组建设过程材料大…

电脑录屏不用愁!四款免费录屏软件深度体验分享

虽然我不是专业的&#xff0c;但是我有一颗想要变得专业的心。作为一名经常需要录制教学视频和游戏直播的博主&#xff0c;我深知一款好用的录屏软件对于工作效率的重要性。今天&#xff0c;我就来和大家分享一下我最近亲测的四款免费录屏软件&#xff0c;来看看哪一款更适合你…

shodan5,参数使用,批量查找Mongodb未授权登录,jenkins批量挖掘

查找美国安全局漏洞 nww.nsa.gov&#xff08;美国安全局官方网站) net参数使用 搜索指定的ip网段 shodan search --limit 10 --fields ip_str,port net:208.88.84.0/24 (老美国家安全局的一个网段)可能直接访问不太行&#xff0c;可以使用host参数&#xff0c;得到域名再去…

部署MiniCPM-V

GitHub - OpenBMB/MiniCPM-V: MiniCPM-V 2.6: A GPT-4V Level MLLM for Single Image, Multi Image and Video on Your Phone 安装和执行 "Local WebUI Demo" 的步骤如下&#xff1a; 克隆仓库并导航到源文件夹&#xff1a; git clone https://github.com/OpenBMB/M…

Vue 权限管理

vue 中&#xff0c;比较常见的需要进行权限管控的权限控制实现思路有四条&#xff1a;、 菜单的控制 在登录请求中&#xff0c;会得到权限数据&#xff0c;当然&#xff0c;这个需要后端返回数据的支持&#xff0c;前端根据权限数据&#xff0c;展示对应的菜单&#xff0c;单…

MongoDB 8.0.3版本安装教程

MongoDB 8.0.3版本安装教程 一、下载安装 1.进入官网 2.选择社区版 3.点击下载 4.下载完成后点击安装 5.同意协议&#xff0c;下一步 6.选择第二个Custon&#xff0c;自定义安装 7.选择安装路径 &#xff01;记住安装路径 8.默认&#xff0c;下一步 9.取…

用于约束多目标优化的新型双阶段双种群进化算法

PPT链接&#xff1a;人工智能论文课程汇报介绍PPT资源-CSDN文库 A Novel Dual-Stage Dual-Population Evolutionary Algorithm for Constrained Multi-Objective Optimization IEEE Transactions on Evolutionary Computation, Volume 26, Issue 5, Pages 1129-1143, October …