qt 信号槽

信号槽

前提:如果没有消息循环,那么Qt的信号和槽无法完全使用。最开始的Qt消息循环开始于QCoreApplication::exec。在绝大部分GUI程序中,GetMessage, DispatchMessage是写在一个死循环中的,除非程序退出,否则会一直处理各种事件。

所谓信号槽(观察者模式),信号本质是事件。

信号:

signals:    

void startGetDataThread();    

void sendPointer(MainWindow*);

1.声明一个信号要使用signals关键字。

2.在signals前面不能使用public、private和protected等限定符,因为只有定义该信号的类及其子类才可以发射该信号。(使用emit函数发射信号)

3.信号只用声明,不需要也不能对它进行定义实现。

4.信号没有返回值,只能是void类型的。

5.只有QObject类及其子类派生的类才能使用信号和槽机制,使用信号和槽,还必须在类声明的最开始处添加Q_OBJECT宏。

6.使用emit 强行发射信号

emit sendPointer(this);

 Qt之所以使用# define emit,是因为编译器并不认识emit,所以把它定义成一个空的宏就可以通过编译。

7. 信号是一个函数, 类的成员函数。所以可以是虚函数的重写

即在基类定义一个纯虚函数,在子类的重写该虚函数,并且声明为信号。

8、信号可以支持重载。

9.在同一个线程中,当一个信号被emit发出时,会立即执行其槽函数,等槽函数执行完毕后,才会执行emit后面的代码。如果一个信号链接了多个槽,那么会等所有的槽函数执行完毕后才执行后面的代码,槽函数的执行顺序是按照它们链接时的顺序执行的。如果信号和槽不是在同一线程,默认情况下,是异步执行,不会阻塞。

10.获取信号发送者

当多个信号连接一个槽时,有时需要判断是哪个对象发来的,那么可以调用sender()函数获取对象指针,返回为QObject指针。

QObject* sender() ;

槽函数

public slots:

void GetDataFromRTDB();

void getPointer(MainWindow *pMainWindow);

1.声明一个槽需要使用slots关键字。

2.一个槽可以是private、public或者protected类型的,

3.槽也可以被声明为虚函数,静态函数、全局函数,这与普通的成员函数是一样的,也可以像调用一个普通函数一样来调用槽。本质就是回调函数。

4.发送者和接受者都需要是QObject的子类(当然,槽函数是全局函数,Lambda表达式等无需接收者的时候除外。

connect():

connect(const QObject *sender, const char *signal,const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);

sender:发出信号的对象

signal:sender对象的信号

receiver:信号接收者

method:receiver对象的槽函数,当检测到 sender 信号,receiver 对象调用 method 方法

ConnectionType: 连接类型,一般不填,为默认值。

  1、Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。

  2、Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数和信号发送者在同一线程。效果看上去就像是直接在信号发送位置调用了槽函数,效果上看起来像函数调用,同步执行。

特点:emit语句后面的代码将在与信号关联的所有槽函数执行完毕后才被执行。

  3、Qt::QueuedConnection:信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。

特点:emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕

  4、Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

  5、Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。

两种表达方式:

1.connect(this,SIGNAL(sendPointer(MainWindow*)),m_pGetDataThreadObj,SLOT(getPointer(MainWindow*)));

2.connect(this,&MainWindow::startGetDataThread,m_pGetDataThreadObj,&DataThreadObject::GetDataFromRTDB);

信号槽的特点:

1.一个信号可以和多个槽相连。

特点:槽函数的执行顺序和信号槽连接的顺序一致。

2.多个信号可以连接到一个槽。

槽函数中判断信号的发出者的办法:首先利用 QObject::setObjectName(const QString&) 方法设置信号发出者的对象名称,

然后在槽函数中利用 QObject::sender()->objectName() 方法获取信号发出者的对象名称。

3.一个信号可以连接到另外的一个信号。

4.信号的参数类型可以与槽的参数类型对应,信号的参数可以比槽的参数多,但不可以少, 否则连接将失败。

5.槽可以被取消连接;这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。可使用disconnect()进行解绑定,其写法和connect一样。

6.信号和槽也不能携带模板类参数。

如果将信号、槽声明为模板类参数的话,即使moc工具不报告错误,也不可能得到预期的结果,也可以取巧,用typedef

typedef pair IntPair;

public slots:

void setLocation (IntPair location);

7.嵌套的类不能位于信号或槽区域内,也不能有信号或槽。即类b嵌套在类a内,想在类b中声明信号与槽是不行的。

8.友元声明不能位于信号或槽声明区内。相反,他们应该在普通C++的private、protected或public区内进行声明

信号与槽的具体流程

可通过moc编译后,得到moc_xxx.cpp文件查看。如下

1.moc查找头文件中的signals,slots,标记出信号和槽。

2.将信号槽信息存储到类静态变量元对象staticMetaObject中,并且按声明顺序进行存放,建立索引。

     注解:QObject类有一个静态成员static const QMetaObject staticQtMetaObject;

   3.当发现有connect连接时,将信号槽的索引信息放到一个map中,彼此配对。

    注解:QObject有一个容器connections,此容器是一个map,key是信号index,value是一个Connection,维护者信号槽的对应关系。

4.当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到QMetaObject(元对象)的active函数。

5.通过active函数找到在map中找到所有与信号对应的槽索引,根据槽索引找到槽函数,执行槽函数。

以上,便是信号槽的整个流程,总的来说就是一个“注册-索引”机制。

static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);

如ColorMaker 有2个信号

signals:

    void valueChanged(int value);

void testChanged(CTest test);

在Widget类进行关联

    ColorMaker *cm = new ColorMaker();

    QObject *object  = cm;

    connect(cm,&ColorMaker::valueChanged,this,&Widget::recv);

    connect(cm,&ColorMaker::testChanged,this,&Widget::recvtest);

经过moc编译后,得到moc_ColorMaker.cpp

// SIGNAL 0

void ColorMaker::valueChanged(int _t1)

{

    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };

    QMetaObject::activate(this, &staticMetaObject, 0, _a);

}

// SIGNAL 1

void ColorMaker::testChanged(CTest _t1)

{

    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };

    QMetaObject::activate(this, &staticMetaObject, 1, _a);

}

经过moc编译后,得到moc_Widget.cpp

void Widget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)

{

    if (_c == QMetaObject::InvokeMetaMethod) {

        auto *_t = static_cast<Widget *>(_o);

        Q_UNUSED(_t)

        switch (_id) {

        case 0: _t->recv((*reinterpret_cast< int(*)>(_a[1]))); break;

        case 1: _t->recvtest((*reinterpret_cast< CTest(*)>(_a[1]))); break;

        case 2: _t->test(); break;

        default: ;

        }

    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {

        switch (_id) {

        default: *reinterpret_cast<int*>(_a[0]) = -1; break;

        case 1:

            switch (*reinterpret_cast<int*>(_a[1])) {

            default: *reinterpret_cast<int*>(_a[0]) = -1; break;

            case 0:

                *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< CTest >(); break;

            }

            break;

        }

    }

}

信号槽机制的优缺点

优点:

Qt信号与槽机制降低了Qt对象的耦合度。

观察者模式,激发信号的Qt对象无须知道是哪个对象的哪个槽函数需要接收它发出的信号,它只需要做的是在适当的时间发送适当的信号就可以了,而不需要知道也不关心它的信号有没有被接收到,更不需要知道哪个对象的哪个槽接收了信号。

缺点:

信号槽机制,同回调函数相比,信号和槽机制运行速度有些慢。遍历,通过传递一个信号来调用槽函数将会比直接调用非虚函数运行速度慢10倍。原因如下:- 需要定位接收信号的对象;- 安全地遍历所有的关联(一个信号关联多个槽的情况);- 编组/解组传递的参数;- 多线程的时候,信号可能需要排队等待。

在没有信号槽机制的时代,C++对象间的交互一般使用回调函数来实现。使用某对象时,用指针指向另一个对象的函数,这个函数就称为回调函数。使用回调函数有个弊端,当某个对象被多个对象通信时,需要一个容器来存放多个对象的回调函数。维护这个容器使得代码编写效率低、扩展性弱。

qt信号槽的本质就是回调函数。

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

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

相关文章

Docker Desktop打开一直转圈的解决办法

安装Docker Desktop之前确保你的Hyper-V已经打开 开启后需要重新安装重新安装重新安装这是最关键的一步&#xff0c;博主自己看了很多教程&#xff0c;最后试着重装了一下解决了 安装DockerDesktop的时候我的电脑根本就没有Hyper-V这个功能选项&#xff0c;可能是这个问题 如…

达梦数据库的DMRMAN工具介绍

达梦数据库的DMRMAN工具介绍 DMRMAN&#xff08;DM RECOVERY MANAGER&#xff09;是 DM 的脱机备份还原管理工具&#xff0c;由它来统一负责库级脱机备份、脱机还原、脱机恢复等相关操作&#xff0c;该工具支持命令行指定参数方式和控制台交互方式执行&#xff0c;降低了用户的…

域名信息查询同款WHOIS源码

域名查询一般是指查询域名的whois注册信息&#xff0c;域名WHOIS是当前域名系统中不可或缺的一项信息服务。在使用域名进行Internet冲浪时&#xff0c;很多用户希望进一步了解域名、名字服务器详细信息 源码免费下载地址抄笔记 (chaobiji.cn)https://chaobiji.cn/

部署轻量级Gitea替代GitLab进行版本控制(二)

version: 3.9 # 创建自定义网络 networks:gitea:name: giteadriver: bridgeservices:## 数据库服务db:image: postgres:latestcontainer_name: gitea_dbrestart: alwaysnetworks:- gitea # 加入到gitea网络ports:- 3003:5432environment:- POSTGRES_USERgitea # PGSQL默认用户-…

ubuntu系统下opencv的编译安装

ubuntu系统下opencv的编译安装 参考https://blog.csdn.net/KIK9973/article/details/118830187 1 安装准备 1.1安装依赖环境(Ubuntu18.04) 下载opencv的依赖&#xff0c;其中第三行的依赖是可选的&#xff0c;前两行的依赖则是必要的。 sudo apt-get install build-essent…

力扣打卡第一天

101. 对称二叉树 C&#xff1a; class Solution { public:bool isSymmetric(TreeNode* root) {return check(root->left,root->right);}bool check(TreeNode *p,TreeNode *q){ /**定义check方法用来检查两棵树是否是镜像的*/if (!p && !q) return true; /* 如…

鸿蒙开发语言_ArkTS开发语言体验_TypeScript语言环境搭建_TS声明和数据类型---HarmonyOS4.0+鸿蒙NEXT工作笔记003

可以看到我们新建的这个项目,有个 @State message: String =Hello ArkTS 这个就是定义了一个变量,可以看到 message是变量名,String是变量类型. 然后我们可以看看它的结构可以看到 build() 下面有个Row,然后再下面有个Column方法,然后,里面就是具体的内容了,首先就是显示了一…

python模拟股票交易

模拟股票交易是一个相对复杂的任务,涉及许多因素,如市场数据获取、交易策略、风险管理等。在Python中,你可以使用多种库和工具来模拟股票交易。以下是一个简单的示例,展示如何使用Python来模拟基本的股票交易。 1. 准备环境 首先,你需要安装一些必要的Python库。你可以使…

Python数据结构【四】排序(二)难度:困难

文章目录 前言一、书接上回二、快速排序&#xff08;Quick Sort&#xff09;2.1 快速排序思想2.2 快速排序代码实现2.3 快速排序复杂度分析 三、堆排序&#xff08;Heap Sort&#xff09;3.1 堆排序思想3.2 堆排序代码实现3.3 堆排序复杂度分析 结语 前言 可私聊进一千多人Pyth…

文件名批量改名,高效将文件名里的符号进行替换删除掉,实现文件名的高效管理

在信息爆炸的时代&#xff0c;我们每天都在与大量的文件打交道。从工作文档到个人照片&#xff0c;从视频剪辑到音频录音&#xff0c;每个文件背后都承载着我们的辛勤付出和美好回忆。然而&#xff0c;随着文件数量的不断增加&#xff0c;如何高效管理这些文件成为了一个亟待解…

MongoDB安装及集成

MongoDB安装及集成 前言 MongoDB是一个开源的、面向文档的 NoSQL 数据库&#xff0c;它采用了 JSON 风格的文档来存储数据&#xff0c;而不是传统的表格形式。MongoDB在数据存储方面具有灵活性和可扩展性&#xff0c;使得它成为了当今流行的数据库之一。 MongoDB的主要特点和…

SQL语言初步认识

1. SQL简介 2. 基本的数据定义 2.1 创建基本表 CREATE TABLE <表名> <列名><数据类型>[<默认值>|<标识列设置>][<该列的完整性约束>] 完整性约束&#xff1a; ①NOT NULL &#xff1a;该列值不能为空 ②NULL &#xff1a;该列值可以为…

关于ERA5气压和温度垂直补偿公式的对比情况

1. 气压和温度垂直补偿对比 「谨代表给个人观点&#xff0c;杠精请自测&#xff0c;对对对&#xff0c;好好好&#xff0c;你说啥都对」。 使用2020-2022陆态网GNSS与探空站并址的48个站点实验&#xff0c;以探空站为真值&#xff0c;验证ERA5精度。怎么确定并址请看前面文章…

C++感受6-Hello World 交互版

变量、常量输入、输出、流getline() 函数读入整行输入Hello() 函数复习新定义函数 Input() 实现友好的人机交互还有 “痘痘” 为什么挤不到的分析…… 1. DRY 原则简介 上一节课&#xff0c;我们写了两版“问候”程序。第一版的最大问题是重复的内容比较多&#xff0c;每一次问…

webAssembly学习及使用rust

学习理解 webAssembly 概念知识&#xff0c;使用 API 进行 web 前端开发。 概念 是一种运行在现代网络浏览器中的新型代码&#xff0c;并且提供新的性能特性和效果。它有一种紧凑的二进制格式&#xff0c;使其能够以接近原生性能的速度运行。C/C、 C#、Rust等语言可以编译为 …

LeetCode题练习与总结:简化路径--71

一、题目描述 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本身&a…

RIP小实验配置及缺省路由下发

配置如下&#xff1a; IP配置&#xff1a; IP配置完先查看RIP协议学习到的路由表&#xff0c;没有内容则代表没有开启RIP 启用RIP&#xff1a;这里的rip后跟的ID只具有本地意义&#xff0c;可以在1-65535之间随便取&#xff0c;不同路由器之间都可以取用不同的&#xff0c;为了…

上网行为管理系统功能介绍_上网行为管理实现的功能

上网行为管理系统是一种集成了网络监控、行为分析、策略管理和安全控制等功能的综合性软件解决方案。 它通过对企业内部网络的全面监控和深度分析&#xff0c;帮助管理者了解员工的网络使用习惯、识别潜在风险、优化网络资源配置&#xff0c;并最终实现网络安全和效率的双重提…

hyperf统一请求响应

2024年4月18日08:48:45 以下是两个方案&#xff1a; 1&#xff0c;使用注解&#xff0c;直接返回 <?phpnamespace App\Utils;use App\Utils\GlobalCode; use App\Utils\GlobalMsg; use Hyperf\Contract\ContainerInterface; use Hyperf\Di\Annotation\Inject; use Hyper…

对接浦发银行支付(三)-- QR扫码付

一、使用场景 扫码付&#xff0c;指的是支付平台&#xff0c;给每个用户的具体订单生成一个QR二维码&#xff0c;用户本人或者他人扫码付款。 付款用户可以直接识别二维码&#xff0c;或者下载到本地&#xff0c;通过微信或支付宝扫一扫识别&#xff0c;第二步将跳转至对应的支…