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 指针来操作界面中拖动创建的控件;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计 | springboot 多功能商城 购物网站(附源码)

1&#xff0c; 概述 国家大力推进信息化建设的大背景下&#xff0c;城市网络基础设施和信息化应用水平得到了极大的提高和提高。特别是在经济发达的沿海地区&#xff0c;商业和服务业也比较发达&#xff0c;公众接受新事物的能力和消费水平也比较高。开展商贸流通产业的信息化…

Java和JavaScript的区别与联系

引言 Java是一种由Sun Microsystems&#xff08;现在是Oracle公司&#xff09;开发的面向对象编程语言&#xff0c;最初于1995年发布。Java被设计为一种跨平台的语言&#xff0c;可以在多个操作系统上运行&#xff0c;这是其广泛应用的重要原因之一。Java具有丰富的标准库和第三…

常见分类网络的结构

VGG16 图片来自这里 MobilenetV3 small和large版本参数,图片来着这里 Resnet 图片来自这里

AutoDL使用conda运行pytorch、dgl

环境配置要是出现兼容问题还是挺繁琐的。所以这里记录下成功的配置情况。 conda create --name Test python3.9 # 构建一个虚拟环境 conda init bash && source /root/.bashrc # 更新bashrc中的环境变量 conda activate Test # 切换到该虚拟环境 pip install torch…

windows安装oracle之后怎么连接使用

目录 1.打开SQl Developer 2.选择JDK 3.登录 4.创建表空间,用户 安装oracle的详细教程 WINDOWS安装Oracle11.2.0.4-CSDN博客 1.打开SQl Developer 找到 SQl Developer 2.选择JDK 根据你安装的oracle版本,因为我的oracle是安装的32位的,所以这里jdk也要选择32位 选择到ja…

私募证券基金动态-23年12月报

成交量&#xff1a;12月日均7,696.93亿元 2023年12月A股两市日均成交7,696.93亿元&#xff0c;环比下降12.39%、同比下降2.26%。12月整体21个交易日&#xff0c;无单日交易日成交金额过万亿&#xff0c;单日交易日最低成交金额为6,122.84亿元&#xff08;12月25日&#xff09;…

【Linux】进程通信——共享内存+消息队列+信号量

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;【LeetCode】winter vacation training 目录 &#x1f449;&#x1f3fb;共享内存&#x1f449;&#x1f3fb;关…

测试用例的设计(超详细)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号&#xff1a;互联网杂货铺&#xff0c;回复1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 1. 测试用例的概念 软件测试人员向被测试系统提供的一…

MySQL窗口函数--lead()函数

lead()函数&#xff1a; 查询当前行向下偏移n行对应的结果 该函数有三个参数&#xff1a;第一个为待查询的参数列名&#xff0c;第二个为向下偏移的位数&#xff0c;第三个参数为超出最下面边界的默认值。 如下代码&#xff1a; 查询向下偏移 2 位的年龄 SELECT user_id,user…

学习Android的第一天

目录 什么是 Android&#xff1f; Android 官网 Android 应用程序 Android 开发环境搭建 Android 平台架构 Android 应用程序组件 附件组件 Android 第一个程序 HelloWorld 什么是 Android&#xff1f; Android&#xff08;发音为[ˈnˌdrɔɪd]&#xff0c;非官方中文…

Linux 驱动开发基础知识——总线设备驱动模型(八)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

SAP SD出库单部分开票后无法继续开票

SAP SD出库单部分开票后无法继续开票。 凭证 80051268 没有包含任何带有未清数量的项目 消息编号 VF171 诊断 凭证80051268不包含可以转换到开票类型中的项目。 系统响应 系统拒绝任何后续处理。 步骤 请检查选择的销售和分销凭证。 除了修改VBUP的相关字段&#xff0c;还有…

服务器未启动而端口进程仍在运行如何查看并杀死

首先登录服务器然后查看当前监听的端口&#xff1a; sudo netstat -tuln比如这里的8080&#xff0c;我们此时并未启动服务器&#xff0c;但是它却正在运行&#xff0c;这会导致服务器刚启动就秒挂。如果没有日志的话会让人有点疑惑&#xff0c;这种情况可能是之前运行了该进程…

外汇天眼:SIX推出了新的SIX参考利率加密货币和SIX实时加密货币指数

全球金融信息提供商SIX今天宣布推出新的SIX参考利率加密货币和SIX实时加密货币指数。新的SIX参考利率加密货币指数和SIX实时加密货币指数涵盖了主要的加密资产比特币&#xff08;BTC&#xff09;和以太坊&#xff08;ETH&#xff09;&#xff0c;为市场及其表现提供了全面的快照…

C/C++ - 函数模板

目录 函数模板基础 函数模板定义 函数模板实例 函数模板调用 函数模板本质 模板函数特化 模板参数限定 默认模板参数 多个模板参数 非类型模板参数 函数模板拓展 模板参数匹配规则 函数模板基础 函数模板定义 使用 template <typename T>​​​​​ 或 templ…

python如何实现异步并发

下面是一个示例代码&#xff0c;展示了如何设计一个异步线程池&#xff0c;并实现线程池满了就等待&#xff0c;空了就继续扔的功能&#xff1a; import concurrent.futures import time # 创建一个线程池 thread_pool concurrent.futures.ThreadPoolExecutor(max_workers8) …

【Java】实现图书管理系统

文章目录 1. 设计背景2. 需求分析3. 设计思路4. 实现4.1 book包4.1.1 Book类4.1.2 BookList类(书架) 4.2 user包4.2.1 User 类4.2.2 AdminUser类&#xff08;管理员用户&#xff09;4.2.3 NormalUser类&#xff08;普通用户&#xff09; 4.3 operation包4.3.1 IOPeration接口4.…

docker笔记整理

Docker 安装 添加yum源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装docker yum -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin 启动docker systemctl start docker 查看docker状态 s…

指针(五)

1. sizeof 和 strlen 的对比 1.1 sizeof sizeof 计算变量所占用内存空间大小的&#xff0c;单位是字节&#xff0c;如果操作数是类型的话&#xff0c;计算的是使用类型创建的变量所占用空间的大小。 sizeof 只关注占用内存空间的大小&#xff0c;不在乎内存中存放了什么数据…

【博士每天一篇论文-算法】Continual Learning Through Synaptic Intelligence,SI算法

阅读时间&#xff1a;2023-11-23 1 介绍 年份&#xff1a;2017 作者&#xff1a;Friedemann Zenke&#xff0c;巴塞尔大学弗里德里希米歇尔研究所(FMI) Ben Poole&#xff0c;谷歌 DeepMind 研究科学家 期刊&#xff1a; International conference on machine learning. PMLR…