QT跨平台应用程序开发框架(3)—— 信号和槽

目录

一,基本概念

二,connect函数使用

2.1 connect

2.2 Qt内置信号和槽

 2.3 一些细节

三,自定义信号和槽

3.1 自定义槽函数

3.2 自定义信号

3.3 带参数的信号槽

四,信号和槽的意义

五,信号和槽断开连接

六,使用lamdba表达式定义槽函数


一,基本概念

  • Qt中,用户和空间的每次交互过程我们称为一个事件,比如“点击按钮”和“关闭窗口”都是一个事件
  • 每个事件发生时都会产生一个信号,比如点击按钮会产生“按钮被点击的信号”,用户关闭窗口会产生“窗口被关闭”的信号,这点我们前面已经介绍过,并且基本概念和Linux的信号有相似之处:Linux系统编程——进程信号-CSDN博客

在Qt中的信号主要涉及到三个要素:

  • 信号源:由哪个空间发出
  • 信号类型:用户进行不同的操作,就可能触发不同的信号
  • 信号的处理方式:就是一个处理函数,这个处理函数在Qt中就叫做“槽函数”(slot),其实就是一个回调函数;Qt 可以用connect将一个信号和一个槽关联起来

注意:必须先把信号和槽用connect关联之后,再触发这个信号才会执行槽函数,顺序不能颠倒 

二,connect函数使用

2.1 connect

这个函数是 QObject 类提供的一个静态成员函数

问题:为什么说是静态呢?

解答

  • 这个QObject 类是其它Qt内置类的“祖宗”
  • 因为Qt的内置类有很多很多继承关系的,而绝大多数的类,往上深扒继承关系,最终都会看到有个 QObject类
  • 所以在很多很多Qt内置内中,都可以直接使用connect这个函数

connect的函数头如下:

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

解释下参数:

  • sender:表示当前信号是哪个控件发出来的
  • signal:表示了当前发生信号的类型,第二个参数的类型必须和第一个参数匹配,比如我创建一个按钮,那么就必须匹配点击事件不能匹配输入事件,毕竟一个按钮无法输入;输入事件也要和输入框匹配
  • receiver:表示这个信号要哪个对象负责处理
  • method:表示这个对象该咋处理
  • type:这个我们不考虑,一般情况下也几乎不会用到这个

基本的使用方式我们上一篇文章已经讲过了:QT跨平台应用程序开发框架(2)—— 初识QT-CSDN博客

上下面我们实现下点击按钮关闭窗口的代码:

这个close也是别人已经实现好了,作用就是关闭窗口,我们直接用就行 

注意:我们在填第二个参数时会显示有两个click:

  • 第一个click:左边小图标是锯齿的,表示这是一个slot函数,作用就是在调用的时候相当于点了一下按钮
  • 第二个clicked:左边的是一个类似信号发射器的一个图标,这个表示信号;我们要选的是第二个,clicked表示过去式,就是“点完了”

2.2 Qt内置信号和槽

Qt 也提供了很多内置的信号和槽让我们使用,但是要想全部记住,几乎是不可能的,所以最好的方式就是多看文档,多写,所谓“熟能生巧” 

在翻文档的时候,如果没有找到对应的条目,可以看看这个类的父类,并且文档也提供了这个类的父类叫什么,以QPushButton为例:

关于这个QAbstractButton类,首先 abstract 这个单词是“抽象的”的意思,因为Qt会提供好几种按钮,这些按钮 中存在一些功能相同的内容,所以就把这些相同的对东西搞出来,单独搞成了一个QAbstractButton这个类,其它很多控件类也是如此的 


然后往下滑,就会看到一些内置的slot和signal,如果没有,就可以去父类看看,比如上面说到的QAbstractButton类,在它的页面往下滑,就可以看到:

再点击对应的绿色的,就能看到更多详细信息,比如clicked:

 2.3 一些细节

我们再观察下connect函数的定义和使用:

问题:char* 和 函数指针是一个东西吗?

解答: 两个都是指针,但是不是同一个类型的指针

  • 我们clicked函数的声明是:
  • 所以指向这个函数的函数指针,也就是取地址后的指针就是 void(*)(),这个和char*是完全不同的,close同理;而在C++中,是不允许用两个不同类型的指针类型相互赋值的(函数传参,本质上就是赋值)

我们可以看下connect在文档中的声明:

 这个函数声明是旧版本的Qt的connect的声明,所以在以前版本的传参和现在有区别:

  • 给信号参数传参时,要搭配一个 SIGNAL 宏,主要是将传入的函数指针转成 char*
  • 给槽函数传参时,要搭配一个 SLOT 宏,同上

所以旧版本需要进行的用法如下:

connect(button, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));

在Qt 5 开始,对上述操作进行了简化,去掉了两个宏,给connect提供了重载版本,第二个参数和第四个参数乘客泛型参数,允许传入任意类型的函数指针

这个就是Qt封装的一个类型萃取器,但是关于类型萃取,涉及模板特化等特性,这里不过多展开

有了泛型参数,此时这个connect函数就带有了一定的“参数检查”功能,如果传入的第一和第二或者第三和第四参数不匹配,那么代码就会直接编译出错,这个是宏做不到或者说很难做到的

三,自定义信号和槽

3.1 自定义槽函数

有时候Qt内置的信号和槽可能无法满足我们的需求,所以我们可以自己实现信号和槽,我们先说自定义槽

我们上篇文章已经用自定义槽实现了按按钮切换按钮里的文本:

所以所谓的槽函数,操作过程和自定义一个普通的成员函数没啥区别


除了上面这种最基础的,还有第二种更便捷的方法

先通过拖拽方式创建一个按钮,右键这个按钮,点击“转到槽”:

比如我实现一个切换窗口标题的按钮:

  • 可以看到,我们通过这种方式生成的槽函数,并没有通过connect关联,因为在 Qt 中,除了通过connect 之外,还可以通过函数名字的方式来自动连接
  • 比如上面的槽函数名字 on_pushButton_clicked ,on相当于固定前缀,中间的 pushButton 是按钮的 objectName ,后面的 clicked 就是信号的名字
  • 所以当函数名符合上述规则之后,Qt就能自动把信号和槽进行关联

问题:如何做到自动识别的?

解答:我们把上面槽函数的声明和定义的clicked都换成click再次执行程序,可以看到输出有个:

 而这个函数正是在 ui_widget.h 中调用的:

3.2 自定义信号

  • 自定义槽函数非常重要,开发中大部分情况都是需要自定义槽函数的,因为槽函数就是用户触发某个操作之后要进行的业务逻辑,而业务逻辑通常是很复杂的,所以需要程序员自己去写
  • 而对于自定义信号,就比较少见,实际开发中很少会需要自定义信号
  • 因为信号对应着用户的某个操作,而在GUI中,用户能进行的操作是可以穷举的,Qt 内置的信号基本已经覆盖到了所有的用户操作

因此使用Qt 的内置信号,足以应付大部分的开发场景了,咱们的Widget虽然还没有定义任何信号,但是由于其继承了 QWidget 和 QBbject, 这里面已经提供了一些信号,可以直接用

但毕竟是大部分,那还有一小部分是内置信号解决不了的,所以Qt还是为我们提供了自定义信号的途径和方式


  • 所谓 Qt 的信号,其实也是一个“函数”,槽函数和普通成员之间没啥差别
  • 但是信号函数,则是一类非常特殊的函数,程序员只要写出函数声明并告诉 Qt 这是一个“信号”即可
  • 这个函数的定义,是 Qt 在编译过程中自动生成的,并且这个过程我们无法干预

信号在 Qt 中是特殊的机制,Qt 生成的信号函数的实现,要配合 Qt 框架做很多既定的操作

然后关于信号函数声明:

  • 信号函数的返回值必须是void
  • 有没有参数都可以,甚至支持重载 

在进行自定义信号创建之前,要明白几点:

  • Qt 中内置的信号,都不需要通过咱们手动通过代码来触发,用户在 GUI 进行操作时,就会自动触发对应信号(发射信号的代码已经内置到 Qt 源码框架中了) 
  • 而对于我们的自定义信号,Qt 有个日俄欧美人你提供了一个关键字 emit ,也有发射的意思,具体用法下面代码会有演示

①首先是声明信号函数 

②然后将自定义信号和自定义槽函数关联起来并使用emit关键字手动触发信号 

③查看结果 


 emit手动触发信号的步骤可以在代码的任意位置,不一定非得在构造函数中

  • 其实在 Qt 5 版本之后,emit现在其实啥也没干,真正的操作都包含在 mySignal 内部生成的函数定义中
  • 就是上面的代码中不写emit,就写个mySignal() 相当于调用这个函数,一样可以完成触发信号
  • 但即使如此,实际开发中建议加上emit,能增加代码可读性,标识这个语句是发射信号
  • emit也可以手动发射Qt内置的信号

3.3 带参数的信号槽

  • 信号和槽可以带参数,当信号带有参数的时候,槽的参数必须和信号的一致
  • 此时发射信号的时候,就可以给信号函数传递实参,与之对应的这个参数就会被传递到对应的槽函数中,就可以起到让信号给槽传参效果了

①先在声明处带上参数(C++中形参的名字可以省略)

②给槽的定义带上参数 

③查看结果 

④也可以通过按钮来切换标题: 

//通过拖拽方式创建按钮后,右键“转到槽”
void Widget::on_pushButton_clicked()
{emit mySignal("把标题设置为标题1");
}void Widget::on_pushButton_2_clicked()
{emit mySignal("把标题设置为标题2");
}


  • Qt中很多内置的信号也是带有参数的,但是这些参数不是我们自己传的,比如 clicked 信号就有一个参数,是bool,表示当前按钮是否处于“选中”状态(对于QPushButton没啥作用,对QCheckBox复选框很有用,后期再讲)
  • 信号函数的参数和槽参数数量可以不一致,但是信号参数数量需要 >= 槽的参数,否则会编译报错

问题:为什么允许信号参数数量 >= 槽参数个数 呢?

解答

  • 一个槽函数可能会绑定多个信号
  • 如果我们严格要求参数个数一致,就意味着信号绑定槽的要求变高了,会有很多麻烦
  • 换而言之,当下的规则就允许一个槽函数能够绑定更多的信号了

个数不一致,槽函数就会按照信号的参数顺序,拿到信号的前 N 个参数,总之就是要确保槽函数的每个参数是有值的

四,信号和槽的意义

信号和槽的主要面对的问题,就是“响应”用户的操作,执行一段业务逻辑(代码):

  • Qt 的这种信号和槽,在各类 GUI 开发的框架中,是一个比较有特色的存在;这个有特色其实是贬义词,因为其它的 GUI 开发框架,搞的方式更简洁一些
  • 网页开发中响应用户操作,主要就是挂“回调函数”,直接将函数赋值给对应的属性,不需要搞一个单独的connect完成上述的信号槽关联(大部分的 GUI 开发框架都是这样搞的)

Qt 这样的信号槽和connect机制,设想是这样的:

  • 解耦合:把触发用户操作的控件 和 对应的处理逻辑进行解耦合
  • “多对多”效果:比如一个信号可以 connect 到多个槽函数上,一个槽函数也可以被多个信号 connect

其实在GUI开发过程中,“多对多”其实是个伪需求,实际开发很少用到,绝大多数情况“一对一”够用了,而且新推出的一些图形化开发框架,很少有再继续支持这种多对多了 

五,信号和槽断开连接

有连接就有断开,假设我们不再继续保持一个信号和槽的连接,那么可以使用 disconnect 函数来取消关联

  • 但是disconnect用的是比较少的,大部分情况下把信号和槽连上之后就不必再管了
  • 主动断开的场景往往是把信号重新绑定到另一个槽函数上

disconnect的用法和connect基本一致:

六,使用lamdba表达式定义槽函数

定义槽函数时,也可以用lamdba表达式的,lamdba表达式主要应用在“回调函数”场景中:C++ —— C++11新增语法-CSDN博客

废话不多说,直接上代码:

  • 另外,我们也要确认捕获到 lamdba 内部的变量是有意义的,因为回调函数的执行时机是不确定的,所以就要保证无论用户点击了按钮,捕获到的变量都能正确使用
  • lamdba 除了可以按照值得方式来捕获变量[=],也可以按照引用得方式来捕获[&],这时捕获到的一般就是各种控件的指针(但是Qt一般不用[&],因为可能会有很多意想不到的报错)
  • 以上面的代码为例,如果是[&],那么运行后点击按钮就会报错,因为我们在构造函数里创建的button声明周期是随构造函数的,构造函数执行完后button就没了,这时候传递给lamdba的引用就是个空引用,直接闪退
  • lamdba是C++11加入的,Qt 5 及更高版本默认就是按照C++11来编译的,如果使用Qt 4 或者更老版本,就需要在 .pro 文件里手动加上 C++11的编译选项哦

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

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

相关文章

聊聊如何实现Android 放大镜效果

一、前言 很久没有更新Android 原生技术内容了,前些年一直在做跨端方向开发,最近换工作用重新回到原生技术,又回到了熟悉但有些生疏的环境,真是感慨万分。 近期也是因为准备做地图交互相关的需求,功能非常复杂&#x…

一、1-2 5G-A通感融合基站产品及开通

1、通感融合定义和场景(阅读) 1.1通感融合定义 1.2通感融合应用场景 2、通感融合架构和原理(较难,理解即可) 2.1 感知方式 2.2 通感融合架构 SF(Sensing Function):核心网感知控制…

golang标准库path/filepath使用示例

文章目录 前言一、常用方法示例1.将相对路径转换为绝对路径2.获取路径中最后一个元素3.获取路径中除去最后一个元素的部分4.路径拼接5.将路径拆分为目录和文件名两部分6.返回一个相对路径7.文件路径遍历8.根据文件扩展名过滤文件9.使用正则表达式进行路径匹配 前言 path/filep…

HBase实训:纸币冠字号查询任务

一、实验目的 1. 理解分布式数据存储系统HBase的架构和工作原理。 2. 掌握HBase表的设计原则,能够根据实际业务需求设计合理的表结构。 3. 学习使用HBase Java API进行数据的插入、查询和管理。 4. 实践分布式数据存储系统在大数据环境下的应用,…

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (三、影视搜索页功能实现)

在HarmonyOS NEXT开发环境中,可以使用nutpi/axios库来简化网络请求的操作。本文将展示如何使用HarmonyOS NEXT框架和nutpi/axios库,从零开始实现一个简单的影视APP,主要关注影视搜索页的功能实现。 为什么选择nutpi/axios? nutpi…

天机学堂3-ES+Caffeine

文章目录 day05-问答系统表 用户端分页查询问题目标效果代码实现 3.6.管理端分页查询问题ES相关 管理端互动问题分页实现三级分类3.6.5.2.多级缓存3.6.5.3.CaffeineTODO:使用Caffeine作为本地缓存,另外使用redis或者memcache作为分布式缓存,构…

重拾Python学习,先从把python删除开始。。。

自己折腾就是不行啊,屡战屡败,最近终于找到前辈教我 第一步 删除Python 先把前阵子折腾的WSL和VScode删掉。还是得用spyder,跟matlab最像,也最容易入手。 从VScode上搞python,最后安装到appdata上,安装插…

智能新浪潮:亚马逊云科技发布Amazon Nova模型

在2024亚马逊云科技re:Invent全球大会上,亚马逊云科技宣布推出新一代基础模型Amazon Nova,其隶属于Amazon Bedrock,这些模型精准切入不同领域,解锁多元业务可能,为人工智能领域带来革新。 带你认识一起了解Amazon Nova…

flutter 装饰类【BoxDecoration】

装饰类 BoxDecoration BoxDecoration 是 Flutter 中用于控制 Container 等组件外观的装饰类,它提供了丰富的属性来设置背景、边框、圆角、阴影等样式。 BoxDecoration 的主要属性 1.color 背景颜色。类型:Color?示例: color: Colors.blu…

Datawhale-self-llm-Phi-4 Langchain接入教程

本项目是一个围绕开源大模型、针对国内初学者、基于 AutoDL 平台的中国宝宝专属大模型教程,针对各类开源大模型提供包括环境配置、本地部署、高效微调等技能在内的全流程指导,简化开源大模型的部署、使用和应用流程,让更多的普通学生、研究者…

某讯一面,感觉问Redis的难度不是很大

前不久,有位朋友去某讯面试,他说被问到了很多关于 Redis 的问题,比如为什么用 Redis 作为 MySQL 的缓存?Redis 中大量 key 集中过期怎么办?如何保证缓存和数据库数据的一致性?我将它们整理出来,…

Python基于Django的图像去雾算法研究和系统实现(附源码,文档说明)

博主介绍:✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&#x1f3…

【开源免费】基于SpringBoot+Vue.JS欢迪迈手机商城(JAVA毕业设计)

本文项目编号 T 141 ,文末自助获取源码 \color{red}{T141,文末自助获取源码} T141,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

NVIDIA发布个人超算利器project digital,标志着ai元年的开启

上图NVIDIA公司创始人兼首席执行官 黄仁勋(Jensen Huang) 这些年被大家熟知的赛博朋克风格一直都是未来的代言词,可以承载人类记忆的芯片,甚至能独立思考的仿生人,现在,随着NVIDIA的project digital发布之后…

海云安开发者安全智能助手D10荣膺 “ AI标杆产品 ” 称号,首席科学家齐大伟博士入选2024年度 “ 十大杰出青年 ”

2024年12月27日,粤港澳大湾区AI领袖峰会在深圳成功举办,大会表彰了在人工智能技术创新、应用实践和产业发展等方面取得优异成绩的企业和个人,深圳海云安网络安全技术有限公司开发者安全智能助手D10荣膺“AI标杆产品”称号。同时,公…

第23篇 基于ARM A9处理器用汇编语言实现中断<五>

Q:怎样修改HPS Timer 0定时器产生的中断周期? A:在上一期实验的基础上,可以修改按键中断服务程序,实现红色LED上的计数值递增的速率,主程序和其余代码文件不用修改。 实现以下功能:按下KEY0…

R语言绘图

多组火山图 数据准备&#xff1a; 将CSV文件同一在一个路径下&#xff0c;用代码合并 确保文件列名正确 library(fs) library(dplyr) library(tidyr) library(stringr) library(ggplot2) library(ggfun) library(ggrepel)# 获取文件列表 file_paths <- dir_ls(path &quo…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(六)

文章目录 一、考试管理模块实现1、添加考试功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、考试管理功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询接口实现2.3.2 后端编辑接口实现2.3.3 后端删除接口实现2.4 效果展示二、代码下载…

HTML中如何保留字符串的空白符和换行符号的效果

有个字符串 储值门店{{thing3.DATA}}\n储值卡号{{character_string1.DATA}}\n储值金额{{amount4.DATA}}\n当前余额{{amount5.DATA}}\n储值时间{{time2.DATA}} &#xff0c; HTML中想要保留 \n的换行效果的有下面3种方法&#xff1a; 1、style 中 设置 white-space: pre-lin…

SpringMVC (2)

目录 1. RequestMapping 注解介绍 2. RequestMapping 使用 3. RequestMapping与请求方式 3.1 RequestMapping 支持Get和Post类型的请求 3.2 RequestMapping 指定接收某种请求 3.3 GetMapping和PostMapping 4. 传参 4.1 通过查询字符串传参 4.2 在 Body 中传参 4.2.1 …