QT学习日记 | 信号与槽

目录

前言

一、初始信号与槽

1、信号与槽的本质

2、信号与槽的使用

3、内置信号、内置槽函数与自定义信号、自定义槽函数

(1)文档查询

(2)自定义信号与内置槽函数的使用

4、信号与槽函数关联关系

5、带参数的信号与槽函数

6、信号与槽函数的重载

二、深剖信号与槽函数细节

1、宏函数 SIGNAL 与 SLOT

2、为啥通过 ui 指针可以操作界面中拖动创建的控件


前言

        本文主要学习QT最重要的机制之一 —— 信号与槽;从认识信号与槽到熟练掌握、深刻理解信号与槽相关细节;

一、初始信号与槽

        不少朋友在听到信号这一词可能会想到 Linux 中的信号机制,本文中的信号与 Linux 中的信号没有任何关系,但是两者之间又非常的相似;当某一时间到来时,操作系统给当前进程发送一个信号,当进程收到这个信号后会调用指定的处理方法来处理这个信号,关于信号更多细节可以关注下面这篇文章;

Linux | 信号-CSDN博客

1、信号与槽的本质

信号本质:在 Qt 中,所谓信号就是某个事件,当某个控件发生了某个事件时,可以根据这个事件做出指定动作;这里的事件就是信号,比如 Qt 中的按钮QPushButton,这个控件被点击,这个点击就是一个事件;

槽本质:在 Qt 中,所谓槽的本质就是收到信号时,我们对应需要进行处理这个信号的动作,还是以上述按钮为例,我们希望用户点击按钮时,窗口的标题会发生改变,这里窗口标题发生改变就是槽函数所处理的动作;

2、信号与槽的使用

        我们通过 connect 来将某个信号与槽函数来进行绑定,从而达到某个信号发出时,指定的槽函数会被调用;connect 函数如下所示;

QMetaObject::Connection QObject::connect

(

const QObject *sender,

const char *signal,

const QObject *receiver,

const char *method,

Qt::ConnectionType type = Qt::AutoConnection

)

        这样拆解不知道看起来是否更加清晰,该函数有五个参数,其中第五个参数一般为缺省即可,因此我们仅仅需要关注前四个参数;

参数一:该参数类型为QObject类的指针,为信号的发出者的地址;

参数二:该参数为 const char* ,该参数为对象发出的信号;

参数三:该参数类型也为QObject类指针,为信号的接收者,也是处理信号的对象;

参数四:该参数为 const char* ,为接收者所要处理的动作;

注意:

1、第一个参数和第三个参数为QObject类,该类为 Qt 提供的一个基类,Qt 为我们提供的所有类都继承自该类,该类可以说是 Qt 所有内置类的 "祖先类";联想一下C++中切片,也就是说派生类可以赋值给基类;

2、参数二和参数四为const char*类型,而实际上,我们传入的信号和槽函数都是一个函数指针,因此在使用时,我们需要使用SIGNAL和SLOTS将对应的函数指针转换成const char*类型;

需求设计:我们在ui页面中拖出一个按钮组件,我们期望按下这个按钮指定的窗口也关闭;

1、我们先创建一个基于Widget类的窗口项目;

2、我们点击widget.ui文件,绘制一个按钮,并设置按钮文本

3、信号与槽函数关联

        注意到这一步时,我们有两种方法,两种方法各有优劣,实际开发中,哪种方法方便用哪种即可;

3.1、代码添加连接

1)切换到 widget.h 文件,写下槽函数声明

        注意这里的函数声明中,有一个我们陌生的关键字,slots,这个是 Qt 自己设置的关键字,同样,我们还可以与 protected、private组合;这个关键字表示声明的是槽函数;在 Qt5 后提出可以省略该关键字;

2)编写槽函数

        这里教大家一个小技巧,我们可以直接选中我们函数声明,按住键盘 alt + enter,可以直接一键生成函数定义;生成的函数定义当然放在widget.cc文件,声明与定义分离,这一点以后就不在重复;

        这里再次补充一下,qDebug是 Qt 为我们提供的打印类,我们使用这个来替代cout;

3)建立连接

注意:这里我们第二个参数和第四个参数并没有使用宏函数SIGNAL()与SLOTS(),这一点我们后面说明; 

参数一:这里我们用ui界面绘制的这个按钮,因此我们在ui这个指针下寻找按钮对象;

参数二:这里clicked表示点击信号,这里点击表示鼠标按下并抬起;同样还有如下信号;

        关于这里的状态切换以及点击带参数这两个可能大家不大理解,这里暂不做介绍,后面讲解复选框自然会介绍;

参数三:处理信号的对象,也就是接收者;

参数四:信号的处理动作;(这里动作也就是槽函数,我们可以使用 Qt 为我们提供的,也可以使用自己写的,这里用自己写的,填写我们刚写下的槽函数地址即可)

        此时,我们想要的功能便实现了,可以点击运行项目,点击按钮后会关闭窗口,并且还会打印内容;接下来介绍第二种方法,我们重新创建一个新的项目;

3.2、自动添加链接

1)还是在ui界面绘制一个按钮,步骤就不再赘述,与上述方法中步骤一相同

2)右击按钮,选择转到槽

        此时会弹出如下窗口,实际上,这里就是选择按钮发出的信号,等同于我们调用connect时前两个参数的填写;这里前五个信号,我们在上述都讲过的,这里不再赘述;我们直接选择clicked;

        此时会在widget.cpp文件自动生成函数定义,且这个函数定义名字是有讲究的,我们不能修改这个槽函数的名字,否则会关联失败,具体原因,后面会详细介绍; 

3)编写槽函数

        到这一步,我们连接就完毕了,所有工作完成;我们可以直接运行程序查看效果;

3、内置信号、内置槽函数与自定义信号、自定义槽函数

        上述案例中,我们使用了内置信号clicked,与自定义槽函数handler,实际上, Qt 也为我们提供了一些内置信号与槽函数,我们可以通过Qt Creator的帮助文档中查询;下面我们以 QpushButton 为例;

(1)文档查询

点击 Qt Creator 左侧帮助一栏;

在索引中输入指定目标,这里输入 QPushButton;

该类相关接口菜单如下(右边那个,用红色框圈住了菜单中常用的);

        这里我们发现,我们并没有看到信号,实际上由于继承这一特性,信号很可能在父类,我们可以通过如下方式找到其父类;

        我们点击找到其父类,我们在父类的属性菜单中,很容易找到了信号与槽函数字段;

        如下所示,这信号不就是我们刚刚讲过的信号吗?我们便可以通过这样的方式进行文档查询;

(2)自定义信号与内置槽函数的使用

        之前那个关闭窗口,我们是使用内置信号 clicked 与自定义槽函数 handler 来实现的;下面我们再使用 自定义信号与内置槽函数 close 再来实现一次;我们首先再次创建一个新项目;

        还是一样,ui界面绘制按钮,如下所示;

        widget.h文件声明信号;

注意:信号仅需声明,无需定义,且无返回值;

        我们在使用connect关联函数;

        这就完了吗?并没有,这里我们仅仅只是进行了信号关联,我们并没有发送这个信号;我们可以使用 emit关键字来发送信号;我们再返回ui页面,右击按钮,转到槽,选择clicked;然后我们再这个槽函数发送信号;

        编译运行程序,此时我们便可以点击按钮,发出自定义信号,接着窗口就会便关闭;

4、信号与槽函数关联关系

一对一:一个信号对应一个槽函数;

一对多:一个信号对应多个槽函数;

多对多:多个信号对应多个槽函数;

        之前,我们都是采用一对一的方式,接下来,我们再来试试一对多;我们再创建一个空项目,继承自widget类;

        绘制出一个按钮,如下所示;

        在widget声明一个信号与两个槽函数;

        实现这两个槽函数,槽函数啥都不干,完成一个打印自己函数名的工作即可;

        为这两个槽函数建立连接,都连接到 mySignal 这一个信号上;

        我们想点击按钮,执行这两个槽函数,因此我们为按钮的点击信号设置一个槽函数;来到设计师界面(ui界面),右击按钮,转到槽,选择clicked信号;并编写点击按钮对应槽函数;

此时我们编译运行程序,我们每次点击按钮都会发送要给 mySignal 信号,而这个信号与 handler1 与 handler2 槽函数都进行了关联,这两个槽函数都会被执行,因此一次点击就会有两个打印结果;

        同样多对多的方式我就不一一演示了;有兴趣的可以自己下去做实验;

拓展:我们不仅可以信号与槽函数进行关联,我们还以信号与信号关联,一个信号被触发,他会触发另一个信号;

5、带参数的信号与槽函数

        我们的信号与槽函数可以带参,通过参数,将信号的参数传递给槽函数;不过我们必须遵守如下规则;

1、参数类型匹配

2、信号的参数个数要大于等于槽函数参数

        我们声明一个信号,两个槽函数,如下所示;

        接下来,我们在.cpp文件中对这两个槽函数进行定义;

        我们在构造函数中连接并发送这个信号;

        接着运行程序,如下所示;

6、信号与槽函数的重载

        信号与槽函数可以进行重载,只不过在重载后,进行connect时,需要显示指定connect哪一个重载函数;

        依然,我们创建一个信号,用这个信号连接两个重载槽函数;首先创建新项目,声明如下信号与槽函数;

        我们在对声明槽函数进行定义,如下所示;

        最后对槽函数进行连接,主要是这里连接的代码,由于函数重载,我们如果直接连接,不知道连接到哪一个槽函数,因此我们需要先显示指定出类型,然后再连接;如下代码所示;

        尤其是红色框内的代码逻辑,函数指针那块代码可能会有些绕;

二、深剖信号与槽函数细节

1、宏函数 SIGNAL 与 SLOT

        前面我们说过connect第二个和第四个参数是 const char* 类型,需要用宏 SIGNAL 与 SLOT 将函数指针转换成 const char* 类型;可我们上述所有代码都没有使用这两个宏函数;这是因为在 Qt5 中,提供了这个函数的另一个版本;

template <typename PointerToMemberFunction>

QMetaObject::Connection QObject::connect

(

const QObject *sender,

PointerToMemberFunction signal,
const QObject *receiver,

PointerToMemberFunction method,

Qt::ConnectionType type = Qt::AutoConnection

)

        这个PointerToMemberFunction是一个模板,这里涉及C++中萃取技术;因此在 Qt5 中,我们通常不用使用这两个宏函数;

2、为啥通过 ui 指针可以操作界面中拖动创建的控件

        首先我们来看 ui 指针到底是什么类型,这个指针声明在 widget.h 文件中;

        它的类型是Ui命名空间里的Widget类指针;注意,我们这个 ui 指针所在类的类名也是Widget,而这个Widget并不在Ui这个命名空间内;那么Ui命名空间的Widget在哪里声明的呢?

        实际上,Qt 采用元编程的技术,所谓元编程,就是用代码生成代码,我们的 Qt 代码首先经过 qmake 编译器,编译出C++代码,再拿这个C++代码生成最后可执行程序;

        如何证明?

        我们编译程序后,会在同级目录下生成一个Build文件夹,这个文件夹就会存放qmake编译生成的代码;

        以HelloWorld项目为例;我们点金对应生成的Build文件夹;

        这个ui_widget.h 文件便是我们 设计师页面,生成的头文件,我们点进这个文件;

        Ui命名空间内的Widget便是继承于Ui_Widget类;

        我们通过拖动控件的方式来构建一个Label标签;如下所示;

        我们重新编译,接着来看Build文件夹下的 Ui_Widget.h 文件;

        这时,我们的 Ui_Widget.h 文件多了一个QLable控件;同样的代码,我们给这个QLabel 改个名字;我们再观察这个文件;我们把这个QLabel控件的名字改为 label_1234;

        我们观察 Ui_Widget.h 文件,发现QLabel标签的名字果然发生了改变;

        综上所述,我们通过ui指针本质就是 Ui_Widget 类型的指针,而这个类是 widget.ui 文件经过qmake编译后,生成了一个Ui_Widget.h 的文件,而这个文件内有 Ui_Widget 这个类;

        而我们 Widget.h 文件中声明的 ui 指针正是Ui命名空间中的 Widget 类,而该类中又有我们通过 Widget.ui 文件中拖动生成的各类控件;故我们可以通过 ui 指针来操作界面中拖动创建的控件;

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

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

相关文章

【软件设计师笔记】程序语言设计考点

【考证须知】IT行业高含金量的证书(传送门)&#x1f496; 【软件设计师笔记】计算机系统基础知识考点(传送门)&#x1f496; 【软件设计师笔记】操作系统考点(传送门)&#x1f496; &#x1f413; 编程语言之间的翻译形式 汇编 高级程序不能直接在计算机上执行&#xff0c;…

yolov8训练自己的关键点检测模型

参考&#xff1a; https://blog.csdn.net/weixin_38807927/article/details/135036450 标注数据集 安装labelme pip install labelme -i https://pypi.tuna.tsinghua.edu.cn/simple如果报错 $ labelme 2024-01-31 03:16:20,636 [INFO ] __init__:get_config:67- Loading …

YOLOv5改进系列(29)——添加DilateFormer(MSDA)注意力机制(中科院一区顶刊|即插即用的多尺度全局注意力机制)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

面试宝典之深谈JVM

面试宝典之深谈JVM 1.为什么需要JVM&#xff0c;不要JVM可以吗&#xff1f; 1.JVM可以帮助我们屏蔽底层的操作系统 一次编译&#xff0c;到处运行 2.JVM可以运行Class文件 2.JDK&#xff0c;JRE以及JVM的关系 3.我们的编译器到底干了什么事&#xff1f; 仅仅是将我们的 .ja…

深入理解 Golang 的 crypto/elliptic:椭圆曲线密码学的实践指南

深入理解 Golang 的 crypto/elliptic&#xff1a;椭圆曲线密码学的实践指南 引言crypto/elliptic 库概览基本使用教程高级应用案例性能与安全考量结论 引言 在当今数字时代&#xff0c;数据安全和加密技术成为了信息技术领域的重中之重。特别是在网络通信和数据存储领域&#…

如何系统的自学Python?通义千问、讯飞星火、文心一言及ChatGPT的回答

如何系统的自学Python&#xff1f;来看看通义千问、讯飞星火、文心一言及ChatGPT的回答. 第一个是马老师的通义千问 系统地自学Python是一个循序渐进的过程&#xff0c;从基础语法到实践项目&#xff0c;再到专业领域的深入学习。下面是一个详细的步骤指南&#xff1a; 了解Py…

控制台npm start终止不了?

控制台npm start终止不了&#xff1f; 在开发的过程中我遇到了这样的问题&#xff0c;想结束控制台3002端口运行&#xff0c;但是ControlC不起作用&#xff0c;不管我敲多少遍&#xff0c;依旧没有任何动静&#xff1a; 再次启动的时候它又会自动启动3003端口&#xff0c;300…

Kotlin 协程:深入理解 ‘async { }‘

Kotlin 协程&#xff1a;深入理解 ‘async { }’ Kotlin 协程是一种强大的异步编程工具&#xff0c;它提供了一种简洁、易读的方式来处理并发和异步操作。在 Kotlin 协程库中&#xff0c;async {} 是一个关键的函数&#xff0c;它允许我们启动一个新的协程&#xff0c;并返回一…

【大厂AI课学习笔记】1.4 算法的进步(2)

关于感知器的兴衰。 MORE&#xff1a; 感知器的兴衰 一、感知器的发明与初期振动 在人工智能的历史长河中&#xff0c;感知器&#xff08;Perceptron&#xff09;无疑是一个里程碑式的存在。它最初由心理学家Frank Rosenblatt在1950年代提出&#xff0c;并在随后的几年中得到…

【数字电子技术课程设计】多功能数字电子钟的设计

目录 摘要 1 设计任务要求 2 设计方案及论证 2.1 任务分析 2.1.1 晶体振荡器电路 2.1.2 分频器电路 2.1.3 时间计数器电路 2.1.4 译码驱动电路 2.1.5 校时电路 2.1.6 整点报时/闹钟电路 2.2 方案比较 2.3 系统结构设计 2.4 具体电路设计 3 电路仿真测试及结…

京东物流基于 StarRocks 的数据分析平台建设

作者&#xff1a;京东物流 数据专家 刘敬斌 小编导读&#xff1a; 京东集团 2007 年开始自建物流&#xff0c;2017 年 4 月正式成立京东物流集团&#xff0c;截至目前&#xff0c;京东物流已经构建了一套全面的智能物流系统&#xff0c;实现服务自动化、运营数字化及决策智能化…

开源编辑器:ONLYOFFICE文档又更新了!

办公软件 ONLYOFFICE文档最新版本 8.0 现已发布&#xff1a;PDF 表单、RTL、单变量求解、图表向导、插件界面设计等更新。 什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器&#xff0c;支持编辑处理文本文档、电子表格、演示文稿、可填写的表单、PDF&#…

Java基础学习:System类和Static方法的实际使用

一、System类 1.在程序开发中&#xff0c;我们需要对这个运行的结果进行检验跟我们预判的结果是否一致&#xff0c;就会用到打印结果在控制台中显示出来使用到了System类。System类定义了一些和系统相关的属性和方法&#xff0c;它的属性和方法都是属于静态的&#xff0c;想使用…

数字孪生 三维建模方式以及细节步骤流程

对于数字孪生这个概念&#xff0c;三维建模不同行业认知都不尽相同。有的行业认为数字孪生重点在于建模&#xff0c;有的行业认为在于物联感知&#xff0c;还有部分认为是虚拟仿真。今天重点从建模角度和大家谈谈数字孪生技术常用的三维建模方式以及精细度分级。 数字孪生平台…

钉钉群机器人-发送群消息

1、钉钉群创建机器人 添加完成后&#xff0c;要记住 Webhook 路径&#xff1b; 2、机器人接入文档网址 自定义机器人接入 - 钉钉开放平台 3、JAVA代码 import com.dingtalk.api.DefaultDingTalkClient; import com.dingtalk.api.DingTalkClient; import com.dingtalk.api.re…

【靶场实战】Pikachu靶场XSS跨站脚本关卡详解

Nx01 系统介绍 Pikachu是一个带有漏洞的Web应用系统&#xff0c;在这里包含了常见的web安全漏洞。 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习&#xff0c;那么Pikachu可能正合你意。 Nx02 XSS跨站脚本概述 Cross-Site Scripting 简称为“CSS”&#xff…

聊聊ClickHouse MergeTree引擎的固定/自适应索引粒度

前言 我们在刚开始学习ClickHouse的MergeTree引擎时&#xff0c;就会发现建表语句的末尾总会有SETTINGS index_granularity 8192这句话&#xff08;其实不写也可以&#xff09;&#xff0c;表示索引粒度为8192。在每个data part中&#xff0c;索引粒度参数的含义有二&#xf…

Flink 流式读取 Debezium CDC 数据写入 Hudi 表无法处理 -D / Delete 消息

问题场景是&#xff1a;使用 Kafka Connect 的 Debezium MySQL Source Connector 将 MySQL 的 CDC 数据 &#xff08;Avro 格式&#xff09;接入到 Kafka 之后&#xff0c;通过 Flink 读取并解析这些 CDC 数据&#xff0c;然后以流式方式写入到 Hudi 表中&#xff0c;测试中发现…

Linux mount

挂载移动硬盘 1、通过 命令 fdisk -l 查看移动硬盘 2、创建 挂载点及文件 mkdir zen 3、mount -t ntfs /dev/sdb1 zen 报错&#xff1a;mount: unknown filesystem type ‘ntfs’ 需要安装ntfs-3g 如下才用编译安装方法&#xff1a; wget https://tuxera.com/opensource/ntf…

基于Java SSM框架实现智能快递分拣系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现智能快递分拣系统演示 JAVA简介 Java主要采用CORBA技术和安全模型&#xff0c;可以在互联网应用的数据保护。它还提供了对EJB&#xff08;Enterprise JavaBeans&#xff09;的全面支持&#xff0c;java servlet API&#xff0c;JSP&#xff08;java serv…