【Qt学习笔记】Qt Creator环境下 信号与槽 详解(自定义信号槽、断连、lambda表达式等)

文章目录

  • 1. 信号槽概念
    • 1.1 信号的本质
    • 1.2 槽的本质
    • 1.3 标准信号槽
    • 1.4 信号槽 实例
  • 2. 自定义信号槽
    • 2.1 自定义槽函数
    • 2.2 自定义信号
    • 2.3 带参 信号槽
  • 3. 信号槽的意义 与 作用
  • 4. 信号槽断连 (了解)
  • 5. lamda表达式的使用
    • 5.1 基本用法
    • 5.2 捕获局部变量
    • 5.3 使用捕获列表捕获变量

1. 信号槽概念

信号槽 是 Qt 框架中一种用于对象间通信的机制。它通过让一个对象发出信号,另一个对象连接到这个信号的槽上来实现通信。信号槽机制是 Qt 的核心特性之一,提供了一种灵活且类型安全的方式来处理事件和数据传递。


1.1 信号的本质

QT中,信号由三部分组成:

  1. 信号源:哪个控件发出的信号
  2. 信类别:什么类别的信号
  3. 信号处理方式:通过信号处理函数,当信号被触发时执行。

1.2 槽的本质

本身就是一个成员函数,负责对QT中产生的信号进行处理。

在编写代码时,槽函数的编写等同于类的成员函数。

举例

  • 有一个按钮,当点击这个按钮时,会关闭当前窗口。
  • connect(pushButton, &QPushButton::clicked, this, &MainWindow::close)
    对于该例来说:
    • 信号源:按钮 pushButton
    • 信号类别:点击信号 clicked
    • 信号处理方式:利用连接connect 与槽函数slot(即close)进行处理

1.3 标准信号槽

即 Qt框架 中内置的信号槽

Qt框架中,存在一些标准信号(Standard Signals)和标准槽(Standard Slots)的已定义信号和槽函数。这些标准信号和槽提供了一些常见的功能,可以在不同的Qt对象之间进行连接。

  • 标准信号如:QPushButton、QLineEdit、QSlider等。
  • 标准槽如:对于QWidget,有show()、hide()、close()等。

1.4 信号槽 实例

我们尝试写一个关闭窗口按钮

举例

我们将使用的标准信号 以及 槽函数:

// 单击按钮后发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close();

我们需要在mainWindow.ui中插入一个QPushButton类(不要忘记更改objectName)
在这里插入图片描述

随后通过 connect函数 将该功能实现出来:

// 单击按钮关闭窗口
connect(ui->closeBtn, &QPushButton::clicked, this, &MainWindow::close);

在上面的例子中:

  • 信号(Signal):&QPushButton::clicked。这里的信号是QPushButton类的clicked信号。当用户点击按钮时,QPushButton对象会发出(emit)一个clicked信号。

  • 槽(Slot):&MainWindow::close。这里的槽是MainWindow类的close成员函数。槽可以是任何普通的成员函数,它被设计为响应特定信号的函数。


2. 自定义信号槽

当QT提供的标准信号槽无法满足需求时,我们可以设计所需的信号与槽的功能,最后通过connect连接以实现功能。

当进行自定义信号槽时,需要遵循以下规则:

  • 当我们编写新的类时,要让其继承Qt的某些标准类
    • 继承自QObject:类必须直接或间接地继承自QObject,以便能够使用信号和槽机制
    • Q_OBJECT 宏:在类的声明中,使用Q_OBJECT宏进行标记。
    • 信号与槽函数声明:在类内部声明自定义信号和槽函数。信号声明只需要在signals部分声明,槽函数声明则需要在public slots或private slots部分声明
// 在头文件派生类的时候,首先如下面的写法引入Q_OBJECT宏:
class MyMainWindow : public QWidget
{
    Q_OBJECT
    // ... ...
}

2.1 自定义槽函数

自定义槽函数有两种写法:

法一

一种是当我们使用代码创建控件时,自定义槽函数后手动连接以及实现。

在这里插入图片描述
随后我们在widget.cpp中手动进行创建按钮以及连接信号的操作。

在这里插入图片描述
结果显示:

在这里插入图片描述


法二

我们通过图形化界面创建控件,并利用Qt Creator的功能自动连接。

在这里插入图片描述
此时Qt Creator会自动生成槽函数的定义与声明:

在这里插入图片描述
且此时,我们只需要实现 on_pushButton_clicked() 函数,不需要进行connect就可以实现按钮的功能。

原因如下:

在这里插入图片描述


2.2 自定义信号

以下是一些自定义信号时的要求注意事项:

  1. 返回值必须是 void 类型(直接不写返回值就行)
  2. 信号需要使用 signals 关键字进行声明, 使用方法类似于public等
  3. 信号函数只需要声明, 不需要定义(没有函数体实现)
  4. 如要要发射自定义信号,一般在信号函数前加emit ,表示发射
    • 发送信号的本质就是调用信号函数
    • emit 关键字只是发出信号的标志,没有特殊含义,可以省略

举例

我们首先在头文件中定义信号
在这里插入图片描述
再用connect将自定义信号与槽连接起来、可以直接在构造函数中发送信号,也可以通过其他函数发送信号,我们这里通过按钮按下的槽函数发送信号。
在这里插入图片描述


2.3 带参 信号槽

信号和槽都可以带有参数、自然也有函数重载,两者之间满足规则:

信号和槽的参数类型需一致,且信号的参数个数不能少于槽(可以多于)。

在这里插入图片描述


3. 信号槽的意义 与 作用

  1. 解耦合
    在传统的编程模式中,一个对象A直接调用另一个对象B的函数,形成强耦合关系。如果对象B的函数接口发生变化,那么对象A也需要相应地修改。这种直接依赖关系使得代码难以维护和扩展。信号槽机制通过信号和槽的连接,实现了对象间的间接通信,降低了对象之间的耦合度。

  2. 易于扩展和维护
    由于信号和槽减少了对象间的直接依赖,当系统需要新增功能或者修改现有功能时,往往只需要添加或修改相关的信号和槽,而不需要对其他组件进行大幅度修改。这大大提高了代码的可维护性和可扩展性。

  3. 支持异步通信
    在传统的同步调用中,调用方必须等待被调用方处理完成后才能继续执行,这在某些情况下会导致程序的响应性不佳。信号槽机制支持异步通信,即发送信号后,发送方可以继续其它操作,而不需要等待接收方处理完毕。这对于提高程序的响应性和性能尤为重要。


4. 信号槽断连 (了解)

Qt中可以使用connect连接信号与槽、同时也可以使用disconnect 断开某个信号与槽的连接

disconnect()函数有几种重载形式,可以根据需要选择使用。

由于一般不用disconnect 断开连接,下面进行简单举例:

示例1:断开特定信号和槽
假设有一个按钮(QPushButton)和一个标签(QLabel),当按钮被点击时,标签的文本会改变。如果在某个时刻想要停止这种行为,可以使用disconnect()断开它们之间的连接:

QPushButton *button = new QPushButton("Click me");
QLabel *label = new QLabel("Hello");

// 连接信号和槽
QObject::connect(button, &QPushButton::clicked, [label]() {
    label->setText("Button Clicked!");
});

// 假设在某个条件下,我们需要断开上面建立的连接
QObject::disconnect(button, &QPushButton::clicked, nullptr, nullptr);

这里,disconnect()的调用断开了button的clicked信号与所有槽之间的连接。

示例2:断开所有与对象相关的连接
如果你想断开一个对象的所有信号与槽的连接,可以简单地传递该对象作为参数给disconnect():

// 断开与button相关的所有信号和槽的连接
QObject::disconnect(button);

这将断开button发出的所有信号与任何槽之间的连接,同时也断开任何信号到button槽的连接。

示例3:断开特定的信号和特定的槽
如果你只想断开一个特定信号与一个特定槽之间的连接,可以这样做:

// 假设有一个自定义槽函数
void customSlot();

// 连接信号和槽
QObject::connect(button, &QPushButton::clicked, this, &MyClass::customSlot);

// 在某个条件下,只断开这个特定的信号和槽的连接
QObject::disconnect(button, &QPushButton::clicked, this, &MyClass::customSlot);

这里,只有button的clicked信号与MyClass的customSlot槽之间的连接被断开。

示例4:使用返回值断开连接
connect()函数返回一个QMetaObject::Connection对象,可以用来在稍后断开连接:

QMetaObject::Connection conn = QObject::connect(button, &QPushButton::clicked, []() {
    qDebug() << "Button clicked!";
});

// 断开连接
QObject::disconnect(conn);

这种方式允许对特定的连接进行更精确的控制。

使用disconnect()可以灵活地管理信号和槽之间的连接状态,根据应用程序的需要动态调整其行为。


5. lamda表达式的使用

这里是关于lamda表达式的一篇文章:

【C++11】lambda表达式 的定义、性质和用法

在Qt 5及以上版本中,connect()函数支持使用lambda表达式作为槽函数。这使得在连接信号和槽时可以直接在参数中编写逻辑处理代码,而不需要定义额外的槽函数。这种方式可以让代码更加紧凑和灵活,特别是当槽函数只在一个地方使用且逻辑简单时。

5.1 基本用法

下面是一个使用lambda表达式作为槽函数的基本示例:

QPushButton *button = new QPushButton("Click me");
QObject::connect(button, &QPushButton::clicked, []() {
    qDebug() << "Button was clicked!";
});

在该例中,当按钮被点击时,会执行lambda表达式内的代码,即打印一条消息到调试控制台。

5.2 捕获局部变量

Lambda表达式可以捕获上下文中的变量,以便在表达式内部使用。例如,假设我们想在按钮点击时改变一个标签(QLabel)的文本:

QPushButton *button = new QPushButton("Change Label");
QLabel *label = new QLabel("Original Text");

QObject::connect(button, &QPushButton::clicked, [label]() {
    label->setText("Text after click");
});

这里,lambda表达式通过捕获label指针,可以在按钮被点击时修改标签的文本。

5.3 使用捕获列表捕获变量

我们知道:Lambda表达式的捕获列表允许以不同的方式捕获变量,例如按值捕获(拷贝)或按引用捕获。使用=捕获所有局部变量的副本,使用&按引用捕获所有局部变量。

int count = 0;
QPushButton *button = new QPushButton("Increase Count");

QObject::connect(button, &QPushButton::clicked, [=]() mutable {
    qDebug() << "Count:" << ++count;
});
  • 注意:由于默认情况下lambda表达式是不允许修改捕获的变量的,如果想在lambda表达式内修改按值捕获的变量,需要在参数列表后添加mutable关键字。但是,这里的count变量实际上是按值捕获的副本,外部的count变量不会被修改。为了确保外部变量也被修改,应该按引用捕获:
int count = 0;
QPushButton *button = new QPushButton("Increase Count");

QObject::connect(button, &QPushButton::clicked, [&]() {
    qDebug() << "Count:" << ++count;
});
  • 使用lambda表达式作为槽函数可以增加代码的灵活性和简洁性,尤其适用于处理简单的逻辑或者当我们不想为了一个简单的操作而专门去定义一个槽函数的情况。

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

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

相关文章

代码随想录算法训练营DAY16 | 二叉树 (3)

一、LeetCode 104 二叉树的最大深度 题目链接&#xff1a;104.二叉树的最大深度https://leetcode.cn/problems/maximum-depth-of-binary-tree/ 思路&#xff1a;采用后序遍历递归求解。 class Solution {int ans 0;public int maxDepth(TreeNode root) {if(root null){retur…

阿里云学生服务器完成验证领取300元无门槛代金券和优惠权益

阿里云高校计划「云工开物」学生和教师均可参与&#xff0c;完成学生认证和教师验证后学生可以免费领取300元无门槛代金券和3折优惠折扣&#xff0c;适用于云服务器等全量公共云产品&#xff0c;订单原价金额封顶5000元/年&#xff0c;阿里云百科aliyunbaike.com分享阿里云高校…

2024等保贯穿总结

严重不符合项&#xff1a; 离职人员不能在报告上签字&#xff01;&#xff01;&#xff01;因为人员离职导致测评人员不够的&#xff08;会开观察项&#xff09; 业务受理人员&#xff1a;管合同的人员、签合同的人员、市场部和人员有关的人员都要写进来 签字的人员一定要有相…

rust语言tokio库底层原理解析

目录 1 rust版本及tokio版本说明1 tokio简介2 tokio::main2.1 tokio::main使用多线程模式2.2 tokio::main使用单线程模式 3 builder.build()函数3.1 build_threaded_runtime()函数新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图…

springboot-web服务迁移Kubernetes

1、搞定基础镜像 docker pull openjdk:8-jre-alpine docker tag openjdk:8-jre-alpine 10.204.82.15/kubernetes/openjdk:8-jre-alpine docker push 10.204.82.15/kubernetes/openjdk:8-jre-alpine 2、springboot-web应用服务打包 3、编写Dockerfile构建镜像 FROM 10.204.82.…

STM32——中断

1 什么是中断 中断&#xff1a;打断CPU执行正常的程序&#xff0c;转而处理紧急程序&#xff0c;然后返回原暂停的程序继续运行&#xff1b; 对于单片机来说&#xff0c;中断是指CPU正在处理某个事件A&#xff0c;发生了另一件事件B&#xff0c;请求CPU迅速去处理&#xff08;…

使用npm包js-web-screen-shot做网页截图,可以对截图加文字,箭头等等,类似于微信截图

<template><div class"m-feedback-wrap" :style"{ top: ${feedbackHeight}px }"><div class"m-feedback-icon-wrap"><el-tooltipclass"item"effect"dark"content"内容"placement"left-…

【数据分享】1929-2023年全球站点的逐年平均风速(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全球气象站…

文心一言 VS 讯飞星火 VS chatgpt (196)-- 算法导论14.3 4题

四、用go语言&#xff0c;给定一棵区间树 T 和一个区间 i &#xff0c;请描述如何在 O(min(n&#xff0c;klgn)) 时间内列出 T 中所有与 i 重叠的区间&#xff0c;其中 k 为输出的区间数。(提示:一种简单的方法是做若干次查询&#xff0c;并且在这些查询操作中修改树&#xff0…

springboot164党员教育和管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

初识NodeJS

本文主要基于极客时间《Nodejs开发实战》课程。 本篇&#xff08;一&#xff09;为课程的第二篇——技术预研篇。 什么是Nodejs? 来源官网&#xff1a; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型&#x…

【网络技术】【Kali Linux】Nmap嗅探(二)多设备扫描

上期实验博文&#xff1a;&#xff08;一&#xff09;简单扫描 一、实验环境 本次实验进行Nmap多设备扫描&#xff0c;实验使用 Kali Linux 虚拟机&#xff08;扫描端&#xff09;、Ubuntu 22.04虚拟机&#xff08;被扫描端1&#xff09;、Ubuntu 18.04虚拟机&#xff08;被扫…

金融信贷风控业务详解

前言 Hi&#xff0c;大家好。今天我要根据以往的工作经验做一个全新的业务——金融风控、信贷风控等风控场景。带大家以全新的角度了解风控&#xff0c;包括风控信贷业务讲解、风控决策树、风控决策流、特征工程、三方数据对比和风控系统搭建等一系列知识。 早期的信贷风控做…

代码随想录算法训练营第30天| 51. N皇后、总结

51. N皇后 完成 思路&#xff1a; 如何用回溯法搜索二维棋盘是这道题目和之前不一样的地方&#xff0c;也是题目的难点。 在树形结构中&#xff0c;同层取同一行棋盘的不同列遍历。每递归一次就往下遍历一行。 代码 class Solution {List<List<String>> res n…

platform tree架构下i2c应用实例(HS3003)

目录 概述 1 探究platform tree下的i2c 1.1 platform tree下的i2c驱动 1.2 查看i2c总线下的设备 1.3 使用命令读写设备寄存器 2 认识HS3003 2.1 HS3003特性 2.2 HS3003寄存器 2.2.1 温湿度数据寄存器 2.2.2 参数寄存器 2.2.3 一个参数配置Demo 2.3 温湿度值转换 2.…

FANUC机器人外部远程启动的相关参数设置示例

FANUC机器人外部远程启动的相关参数设置示例 如下图所示,在MENU---设置---选择程序中,设置程序选择模式:RSR(这个根据自己实际使用的自动启动方式来决定,你用RSR选RSR,用PNS就选PNS), 自动运行开始方法:选择UOP,即RSR1-RSR8的启动信号分别对应UI9-UI16, 最后,点击…

一个 SpringBoot 项目能同时处理多少请求?

目录 1 问题分析 2 Demo 3 答案 4 怎么来的&#xff1f; 5 标准答案及影响参数一Tomcat配置 6 影响参数二 Web容器 7 影响参数三 Async 1 问题分析 一个 SpringBoot 项目能同时处理多少请求&#xff1f; 不知道你听到这个问题之后的第一反应是什么&#xff1f; 我大概…

从零开始手写mmo游戏从框架到爆炸(十)— 集成springboot-jpa与用户表

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 集成springboot-jpa&#xff0c;不用mybatis框架一个是方便对接不同的数据源。第二个目前规划的游戏内容可能对数据库的依赖不是很大&#xff0c;jpa应该肯定能满足要求了…

redis之布隆过滤

目录 1、redis之布隆过滤 2、布隆过滤器原理 3、布隆过滤器使用步骤 初始化bitmap 添加占坑位 判断是否存在圜 1、redis之布隆过滤 布隆过滤&#xff1a;有一个初值都为0的bit数组和多个哈希函数构成&#xff0c;用来快速判断集合中是否存在某个元素。目的&#xff1a;减…

【STL】list模拟实现

vector模拟实现 一、接口大框架函数声明速览二、结点类的模拟实现1、构造函数 三、迭代器类的模拟实现1、迭代器类存在的意义2、迭代器类的模板参数说明3、构造函数4、运算符的重载&#xff08;前置和后置&#xff09;&#xff08;1&#xff09;前置&#xff08;2&#xff09;后…