【Qt学习笔记】(二)信号和槽

信号和槽

  • 1 信号和槽概述
  • 2 信号和槽的使用
  • 3 可视化生成槽函数
  • 4 自定义信号和槽
  • 5 带参数的信号和槽
  • 6 信号与槽的连接方式
  • 7 信号与槽的断开
  • 8 使用 Lambda 表达式来定义槽函数

1 信号和槽概述

  • 在Qt中,用户和控件的每次交互过程称为一个事件。比如"用户点击按钮"是一个事件,"用户关闭窗口"也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出"按钮被点击"的信号,用户关闭窗口会发出"窗口被关闭"的信号。
  • Qt中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到"按钮被点击"的信号后,会做出"关闭自己"的响应动作;再比如输入框自己接收到"输入框被点击"的信号后,会做出"显示闪烁的光标,等待用户输入数据"的响应动作。在Qt中,对信号做出的响应动作就称之为槽。
  • 信号和槽是Qt特有的消息传输机制,它能将相互独立的控件关联起来。比如,"按钮"和"窗口"本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响。通过信号和槽机制,可以将"按钮"和"窗口"关联起来,实现"点击按钮会使窗口关闭"的效果。

在这里插入图片描述
信号的本质
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时Qt对应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件。如:

  • 按钮单击、双击
  • 窗口刷新
  • 鼠标移动、鼠标按下、鼠标释放,
  • 键盘输入

在 Qt 中信号是通过什么形式呈现给使用者呢?

  • 我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件。
  • 对于使用者来说触发了一个事件我们就可以得到Qt框架给我们发出的某个特定信号。
  • 信号的呈现形式就是函数,也就是说某个事件产生了,Qt 框架就会调用某个对应的信号函数,通知使用者。

槽的本质

槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何位置( public、protected 或private), 可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。

说明
(1) 信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。 例如: "按钮被按下"这个信号可以用clicked()函数表示,"窗口关闭"这个槽可以用close()函数表示,假如使用信号和槽机制实现: "点击按钮会关闭窗口"的功能,其实就是clicked()函数调用close()函数的效果。

(2) 信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:

  • 信号函数用signals 关键字修饰,槽函数用public slotsprotected slots或者private slots修饰。signals 和slots是Qt在C++的基础上扩展的关键字,专门用来指明信号函数和槽函数;
  • 信号函数只需要声明,不需要定义(实现) , 而槽函数需要定义(实现)。

信号函数的定义是Qt自动在编译程序之前生成的.编写Qt应用程序的程序员无需关注.这种自动生成代码的机制称为元编程(Meta Programming) .这种操作在很多场景中都能见到.

2 信号和槽的使用

连接信号和槽
在 Qt 中,QObject 中提供了一个静态成员函数 connect() ,该函数专门用来关联指定的信号函数和槽函数。

                  关于 QObject

QObject 是 Qt 内置的父类,Qt 中提供的很多类都是直接或者间接继承自 QObject,比如 QWidget 就继承自 QObject

以下是 connect 的函数原型

1 connect (const QObject *sender,
2 const char * signal ,
3 const QObject * receiver ,
4 const char * method ,
5 Qt::ConnectionType type = Qt::AutoConnection )

参数说明:

  • sender:信号的发送者;
  • signal:发送的信号(信号函数) ;
  • receiver:信号的接收者;
  • method:接收信号的槽函数;
  • type:用于指定关联方式,默认的关联方式为Qt::AutoConnection,通常不需要手动设定。

connect 要求第一个参数和第二个参数是匹配的,比如第一个参数的类型如果是 QPushButton* ,那么,第二个参数的信号必须是 QPushButton 内置的信号(父类的信号),不能是其他的一个类。

实现一个点击按钮关闭窗口的功能

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//要实现的功能是点击“关闭”按钮,窗口关闭QPushButton* button = new QPushButton(this);//创建一个button对象button->setText("关闭");//设置button上的文字button->move(200,200);//设置button的位置connect(button,&QPushButton::clicked,this,&Widget::close);
}Widget::~Widget()
{delete ui;
}

首先看connect,第一个参数和第二个参数要求是匹配的,表示button对象发出了clicked 信号,注意这里是 clicked,而不是 click
在这里插入图片描述
第三个参数表示信号的接收者,填写 this,Widget 接收
第四个参数表示接收信号的槽函数,也就是收到信号了以后接收者怎么处理。close 是 Qt 内置的槽函数,Widget 继承自 QWidget ,也就继承了父亲的槽函数,该槽函数的功能 Qt 内部已经实现好了。

运行程序后,点击按钮成功关闭窗口
在这里插入图片描述
关于connect函数的问题
在查看官方文档的时候,connect的原型如下
在这里插入图片描述
第二个参数和第四个参数是 char* 类型,而我们直接在用的时候传的确是函数指针。在C++中,是不允许使用两个不同的指针类型相互赋值的。
官方文档里的这个函数声明是旧版本的 connect 声明 ,正确写法是
在这里插入图片描述
旧版本要搭配 SIGNAL 和 SLOT 这两个宏。从 Qt5 开始,对该写法进行了简化,不再需要写这两个宏了,所以上面的代码才能直接传入函数指针。Qt5 开始,给 connect 提供了重载版本,第二个参数和第四个参数成了泛型参数,允许传入任意类型的函数指针了。
点击 ctrl 然后鼠标点击 connect ,跳转到源码中查看。
在这里插入图片描述
此时,connect 函数就带有一定的参数检查功能,如果传入的第一个参数和第二个参数不匹配,或者第三个参数和第四个参数不匹配。(不匹配指的是,第二个参数和第四个参数的函数指针不是第一个参数和第三个参数的成员函数),此时代码编译出错。

3 可视化生成槽函数

在 ui 界面,拖动 pushbutton 到编辑界面然后输入文字后,右键单击该按钮,选择 “转到槽”
在这里插入图片描述
这个窗口为我们提供了 QPushButton 的所有信号,还包括了 QPushButton的父类信号
选择第一个信号,点击确定
在这里插入图片描述
就会自动在 widget.cpp 文件里面为我们生成对应的槽函数
在这里插入图片描述
现在实现这样的功能:点击按钮,就会改变窗口的标题。
在这里插入图片描述
运行程序,点击按钮。窗口的标题改变。
在这里插入图片描述
为什么这种方式没有调用 connect 函数,却还是能将信号和槽联系起来呢?
原因是 Qt 对于这种命名规则的槽函数,会自动和对应信号进行连接。
说明:
自动生成槽函数的名称有一定的规则。槽函数的命名规则为: on_ XXX_ SSS, 其中:

  • 1、以"on "开头,中间使用下划线连接起来;
  • 2、" XXX "表示的是对象名(控件的obj ectName|属性)。
  • 3、" SSS "表示的是对应的信号。
  • 如: " on_ pushButton_ _clicked() " , pushButton代表的是对象名,clicked 是对应的信号。

4 自定义信号和槽

在以上的内容中,我们知道槽函数不仅仅可以用 Qt 内置的函数,还可以自定义。信号也可以由我们自定义,不过 Qt 提供的信号在大多数情况下已经足够我们使用了。本节内容是如何由自定义信号和槽。
在Qt中,允许自定义信号的发送方以及接收方,即可以自定义信号函数和槽函数。但是对于自定义的信号函数和槽函数有一定的书写规范。
自定义信号函数书写规范:

  • 自定义函数信号必须写到 “signals” 下
  • 返回值为 void ,只需要声明,不需要实现
  • 可以有参数,也可以发生重载

自定义槽函数书写规范:

  • 早期的 Qt 版本要求槽函数必须写到 “public slots” 下,但是高版本的 Qt 允许写到类的 “public” ,作用域或者全局下
  • 返回值为 void ,需要声明,也需要实现
  • 可以有参数,可以发生重载

发送信号

  • 使用 “emit” 关键字发送信号。“emit” 是一个空的宏。“emit” 其实是可选的,没有什么含义,只是为了提醒开发人员

示例1:
在 widget.h 中声明自定义信号和槽,如图所示:
在这里插入图片描述
在 widget.cpp 中实现槽函数,并且关联信号和槽
在这里插入图片描述
运行
在这里插入图片描述

5 带参数的信号和槽

Qt的信号和槽也支持带有参数,同时也可以支持重载.
此处我们要求,信号函数的参数列表要和对应连接的槽函数参数列表一致.
此时信号触发,调用到槽函数的时候,信号函数中的实参就能够被传递到槽函数的形参当中.

通过这样的机制,就可以让信号给槽传递数据了.

示例1:重载信号槽
在"widget.h"头文件中声明重载的信号函数以及重载的槽函数;如下图所示:
在这里插入图片描述

在"Widget.cpp"文件实现重载槽函数以及连接信号和槽。

注意:在定义函数指针时要指明函数指针的作用域。

在这里插入图片描述
执行结果如下图所示:
在这里插入图片描述
示例2:信号槽参数列表匹配规则

在"widget.h"头文件中声明信号和槽函数;
在这里插入图片描述
在"widget.cpp"文件中实现槽函数以及连接信号和槽;
在这里插入图片描述

其实信号的参数个数可以多于槽函数的参数个数,但是槽的参数个数不能多于信号参数个数. 但是实际开发中最好还是保持参数个数也能匹配一致.

6 信号与槽的连接方式

一对一
主要有两种形式,分别是:一个信号连接一个槽和一个信号连接一个信号。
一个信号连接一个槽
在这里插入图片描述
示例:
在"widget.h"中声明信号和槽以及信号发射函数;
在这里插入图片描述
在"widget.cpp"中实现槽函数,信号发射函数以及连接信号和槽;
在这里插入图片描述
一个信号连接另一个信号
在这里插入图片描述
示例:
在.上述示例的基础上,在"widget.cpp"文件中添加如下代码:
在这里插入图片描述

一对多
一个信号连接多个槽
在这里插入图片描述
示例:
在"widget.h"头文件中声明一个信号和三个槽;
在这里插入图片描述
在"widget.cpp"文件中实现槽函数以及连接信号和槽;
在这里插入图片描述
多对一

多个信号连接一个槽函数
在这里插入图片描述
示例:
在"widget.h"头文件中声明两个信号以及一个槽;
在这里插入图片描述
在"widget.cpp"文件中实现槽函数以及连接信号和槽;
在这里插入图片描述

7 信号与槽的断开

使用disconnect即可完成断开.
disconnect的用法和connect基本一致.

示例:
在这里插入图片描述

8 使用 Lambda 表达式来定义槽函数

Lambda 表达式具体的语法规则可以查看这篇文章 Lambda表达式,本节简单介绍一下。

Qt5在Qt4的基础上提高了信号与槽的灵活性,允许使用任意函数作为槽函数。
但如果想方便的编写槽函数,比如在编写函数时连函数名都不想定义,则可以通过Lambda表达式来达到这个目的。

Lambda表达式是C++11增加的特性。C++11 中的Lambda表达式用于定义并创建匿名的函数对
象,以简化编程工作。
Lambda表达式的语法格式如下:

[ capture ] ( params ) opt -> ret { Function body; 
};

在这里插入图片描述
局部变量引入方式 [ ]
[ ]:标识一个Lambda表达式的开始。不可省略。

在这里插入图片描述

  • 由于使用引用方式捕获对象会有局部变量释放了而L ambda函数还没有被调用的情况。如果执行Lambda函数,那么引用传递方式捕获进来的局部变量的值不可预知。所以绝大多数场合使用的形式为:={}
  • 早期版本的Qt,若要使用Lambda表达式,要在"pro"文件中添加:
CONFIG += C++11

因为Lambda表达式是C++11标准提出的。Qt5 以上的版本无需手动添加,在新建项目时会自动添加。

在这里插入图片描述
示例:Lambda 表达式的使用

使用 Lambda 表达式的方式填写槽函数。这里的功能是点击一下按钮,按钮就移动到相应位置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

实时时钟芯片DS1307单片机C语言驱动程序

实时时钟RTC相关索引 1.单片机RTC及时钟芯片的时间到底从哪一年起始&#xff1f; 2.STM32F103单片机内部RTC实时时钟驱动程序 3.实时时钟芯片DS1302单片机C语言驱动程序 4.实时时钟芯片DS1307单片机C语言驱动程序 一、DS1307简介 DS1307是一款非易失性实时时钟&#xff08;R…

代码随想录算法训练营第五十一天 | 309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 题目链接&#xff1a;309.最佳买卖股票时机含冷冻期I 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 *i* 天的股票价格 。 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xf…

【Java程序设计】【C00209】基于SSM个人求职管理系统(论文+PPT)

基于SSM个人求职管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这个一个基于SSM的个人求职管理系统&#xff0c;本系统共分为三种权限&#xff1a;管理员、普通管理员、用户 管理员&#xff1a;首页、个人中心、用户管理、管理…

Day 31 | 贪心算法 理论基础 、455.分发饼干 、 376. 摆动序列 、 53. 最大子序和

理论基础 文章讲解 455.分发饼干 题目 文章讲解 视频讲解 思路&#xff1a;从小饼干开始喂小胃口 class Solution {public int findContentChildren(int[] g, int[] s) {Arrays.sort(g);Arrays.sort(s);int start 0;int count 0;for (int i 0; i < s.length &&a…

go并发编程-介绍与Goroutine使用

1. 并发介绍 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中的多个…

Go语言的100个错误使用场景(11-20)|项目组织和数据类型

前言 大家好&#xff0c;这里是白泽。 《Go语言的100个错误以及如何避免》 是最近朋友推荐我阅读的书籍&#xff0c;我初步浏览之后&#xff0c;大为惊喜。就像这书中第一章的标题说到的&#xff1a;“Go: Simple to learn but hard to master”&#xff0c;整本书通过分析100…

DevSecOps 参考模型介绍

目录 一、参考模型概述 1.1 概述 二、参考模型分类 2.1 DevOps 组织型模型 2.1.1 DevOps 关键特性 2.1.1.1 模型特性图 2.1.1.2 特性讲解 2.1.1.2.1 自动化 2.1.1.2.2 多边协作 2.1.1.2.3 持续集成 2.1.1.2.4 配置管理 2.1.2 DevOps 生命周期 2.1.2.1 研发过程划分…

leetcode刷题(剑指offer)54.螺旋矩阵

54.螺旋矩阵 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;ma…

npm 安装时卡在 sill idealTree buildDeps 不动

网上很多博客给出的解决方案是将镜像源修改为淘宝镜像源&#xff0c;如下&#xff1a; 1. 查看当前的镜像网址 npm config get registry 2. 把npm的镜像地址改成taobao的 npm config set registry https://registry.npm.taobao.org3. 检验是否设置成功 npm config get regi…

Android升级版本兼容问题

1、JDK的选择 AndroidJavaAPI and language features supported14 (API 34)17Core libraries13 (API 33)11Core libraries12 (API 32)11Java API11 and lowerAndroid versions https://developer.android.com/build/jdks The following table lists which version of Gradle…

Java基础-集合框架

集合框架&#xff1a; 内存层面可考虑的数据存储容器&#xff1a;数组&#xff0c;集合 数组的特点&#xff1a;长度&#xff0c;存储元素类型确定&#xff0c;既可以放基本数据类型&#xff0c;也可以放引用数据类型 缺点&#xff1a;长度不可变&#xff0c;存储元素特点单…

从零开始 Linux(一):基础介绍与常用指令总结

从零开始 Linux 01. 概念理解 1.1 什么是 Linux&#xff1f; Linux 是一个开源免费的 操作系统&#xff0c;具有很好的稳定性、安全性&#xff0c;且有很强的处理高并发的能力 Linux 的应用场景&#xff1a; 可以在 Linux 下开发项目&#xff0c;比如 JavaEE、大数据、Python…

Arduino 官网上下载和使用开发板

在 Arduino 官网上下载和使用开发板可以按照以下步骤进行&#xff1a; 打开浏览器&#xff0c;访问 Arduino 官网&#xff08;https://www.arduino.cc/&#xff09;。在官网首页&#xff0c;可以看到各种型号的 Arduino 开发板和相关产品。根据自己的需求选 择合适的开发板型号…

3D词云图

工具库 tagcanvas.min.js vue3&#xff08;框架其实无所谓&#xff0c;都可以&#xff09; 实现 <script setup> import { onMounted, ref } from vue; import ./tagcanvas.min.js;const updateFlag ref(false);// 词云图初始化 const initWordCloud () > {let …

RabbitMQ快速实战

目录 什么是消息队列&#xff1f; 消息队列的优势 应用解耦 异步提速 削峰填谷 总结 主流MQ产品特点比较 Rabbitmq快速上手 创建用户admin Exchange和Queue Connection和Channel RabbitMQ中的核心概念总结 什么是消息队列&#xff1f; MQ全称Message Queue&#xf…

Python 开发学习目录

Python 开发教程的概述&#xff1a; 安装 Python&#xff1a;首先&#xff0c;你需要在你的计算机上安装 Python。你可以从 Python 的官方网站上下载并安装 Python。 学习基础语法&#xff1a;学习 Python 的基础语法&#xff0c;包括变量、数据类型、控制结构、函数等。 学习…

关于mysql 条线添加 子查询 包含groupby 导致问题

直接简约明确说明一下这个问题&#xff0c;发生这个问题我也很懵逼。然后各种怀疑自己&#xff0c;最终我猜测了一下可能是这个问题&#xff0c;结果直接再子查询中有嵌套了一个括号解决此问题 之前写的sql 由于公司安全问题不展示&#xff0c;写个伪sql代表一下 selectid from…

Redis - 多集群数据源配置

目录 前言依赖yml配置redis多集群数据源配置类思考 redis工具类 前言 工作时有一个项目配置了多个redis数据源&#xff0c;使用时出现了指定了使用副数据源&#xff0c;数据却依然使用了主数据源的情况。经过排查&#xff0c;发现配置流程较为繁琐易错&#xff0c;此处做一个记…

Spring5深入浅出篇:Spring中ioc(控制反转)与DI(依赖注入)

Spring5深入浅出篇:Spring中ioc(控制反转)与DI(依赖注入) 反转(转移)控制(IOC Inverse of Control) 控制&#xff1a;对于成员变量赋值的控制权 反转控制&#xff1a;把对于成员变量赋值的控制权&#xff0c;从代码中反转(转移)到Spring⼯⼚和配置⽂件中完成好处&#xff1a;…

七、并发工具(上)

一、自定义线程池 1&#xff09;背景&#xff1a; 在 QPS 量比较高的情况下&#xff0c;我们不可能说所有的访问都创建一个线程执行&#xff0c;这会导致内存占用过高&#xff0c;甚至有可能出现 out of memory另外也要考虑 cpu 核数&#xff0c;如果请求超过了cpu核数&#…