4.QT-信号和槽|存在意义|信号和槽的连接方式|信号和槽断开|lambda表达式|信号和槽优缺点(C++)

news2025/4/22 3:26:23
信号和槽存在意义

所谓的信号槽,终究要解决的问题,就是响应用户的操作
信号槽,其实在GUI开发的各种框架中,是一个比较有特色的存在
其他的GUI开发框架,搞的方式都要更简洁一些~~ 网页开发 (js + dom api)
网页开发中响应用户操作,主要就是挂回调函数

button.onclick = handle;

function handle(){
	...
}

处理函数就像控件的一个属性/成员一样
不需要搞一个单独的connect完成上述的信号槽连接~
一对一.
一个事件,只能对应一个处理函数
一个处理函数也只能对应到一个事件上.

Qt信号槽,connect这个机制,设想很美好的~^

  1. 解耦合.把触发用户操作的控件和处理对应用户的操作逻辑解耦合.
  2. "多对多"效果,一个信号,可以connect到多个槽函数上. 一个槽函数,也可以被多个信号connect.
    Qt中谈到的信号和槽"多对多”就和数据库中的多对多非常类似的

一个信号,可以connect到多个槽函数上
一个槽函数,也可以被多个信号connect
![[Pasted image 20250418082032.png]]

综上,Qt引I入信号槽机制,最本质的目的(初心)
就是为了能够让信号和槽之间按照“多对多”的方式来进行关联~~ 其他的GUI框架往往也不具备这样的特性~
其实在GUI开发的过程中,“多对多”这件事,其实是个“伪需求
实际开发很少会用到
绝大部分情况,一对一就够用了.

信号与槽的连接⽅式

⼀对⼀

主要有两种形式,分别是:⼀个信号连接⼀个槽和⼀个信号连接⼀个信号

  1. ⼀个信号连接⼀个槽
    ![[Pasted image 20250417223759.png]]

  2. ⼀个信号连接另⼀个信号
    ![[Pasted image 20250417223824.png]]

⼀对多

⼀个信号连接多个槽
![[Pasted image 20250417223843.png]]

多对⼀

多个信号连接⼀个槽函数
![[Pasted image 20250417223859.png]]

信号与槽的断开

使⽤disconnect即可完成断开.
disconnect的⽤法和connect基本⼀致
widget.h

#ifndef WIDGET_H
#define WIDGET_H
  
#include <QWidget>
  
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
  
class Widget : public QWidget
{
    Q_OBJECT
  
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
  
    void handleClick();
  
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
  
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
}
  
Widget::~Widget()
{
    delete ui;
}
  
void Widget::handleClick()
{
    this->setWindowTitle("修改窗口标题");
}

![[Pasted image 20250418084654.png]]

添加一个修改槽函数的Button
![[Pasted image 20250418084857.png]]

widget.h

#ifndef WIDGET_H
#define WIDGET_H
  
#include <QWidget>
  
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
  
class Widget : public QWidget
{
    Q_OBJECT
  
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
  
    void handleClick();
    void handleClick2();
  
private slots:
    void on_pushButton_2_clicked();
  
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
  
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
}
  
Widget::~Widget()
{
    delete ui;
}
  
void Widget::handleClick()
{
    this->setWindowTitle("修改窗口标题");
}
  
void Widget::handleClick2()
{
    this->setWindowTitle("修改窗口标题2");
}
  
  
void Widget::on_pushButton_2_clicked()
{
    //1.先断开pushButton原来的信号槽
    disconnect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
    //2.重新绑定信号槽
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick2);
}

![[Pasted image 20250418085159.png]]

void Widget::handleClick()
{
    this->setWindowTitle("修改窗口标题");
    qDebug() << "handleClick";
}
  
void Widget::handleClick2()
{
    this->setWindowTitle("修改窗口标题2");
    qDebug() << "handleClick2";
}

![[Pasted image 20250418085721.png]]

如果切换的时候不disconnect,到时候一个信号就会触发两个槽函数,点一下第一个按钮qdebug会同时输出handleClick和handleClick2

使用lambda表达式定义槽函数

Qt5在Qt4的基础上提⾼了信号与槽的灵活性,允许使⽤任意函数作为槽函数。
但如果想⽅便的编写槽函数,⽐如在编写函数时连函数名都不想定义,则可以通过Lambda表达式来达到这个⽬的。
Lambda表达式是C++11增加的特性。C++11中的Lambda表达式⽤于定义并创建匿名的函数对
象,以简化编程⼯作。
Lambda表达式的语法格式如下

[ capture ] ( params ) opt -> ret {  
	Function body;  
};
capture捕获列表
params参数表
opt函数选项
ret返回值类型
Function body函数体
1、局部变量引⼊⽅式[]

[]:标识⼀个Lambda表达式的开始。不可省略。

符号说明
[]局部变量捕获列表。Lambda表达式不能访问外部函数体的任何局部变量
[a]在函数体内部使⽤值传递的⽅式访问a变量
[&b]在函数体内部使⽤引⽤传递的⽅式访问b变量
[=]函数外的所有局部变量都通过值传递的⽅式使⽤,函数体内使⽤的是副本
[&]以引⽤的⽅式使⽤Lambda表达式外部的所有变量
[=,&foo]foo使⽤引⽤⽅式,其余是值传递的⽅式
[&,foo]foo使⽤值传递⽅式,其余引⽤传递
[this]在函数内部可以使⽤类的成员函数和成员变量,=和&形式也都会默认引⼊

说明:

  • 由于使⽤引⽤⽅式捕获对象会有局部变量释放了⽽Lambda函数还没有被调⽤的情况。如果执⾏Lambda函数,那么引⽤传递⽅式捕获进来的局部变量的值不可预知。所以绝⼤多数场合使⽤的形式为: [=] () { }
  • 早期版本的Qt,若要使⽤Lambda表达式,要在".pro"⽂件中添加: CONFIG += C++11
    因为Lambda表达式是C++11标准提出的。Qt5以上的版本⽆需⼿动添加,在新建项⽬时会⾃动添加
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(200, 200);
    connect(button, &QPushButton::clicked, this, [](){
        qDebug() << "lambda 被执行了!";
    });
}
  
Widget::~Widget()
{
    delete ui;
}

![[Pasted image 20250418091847.png]]

![[Pasted image 20250418093924.png]]

lambda表达式,是一个回调函数
这个函数,无法直接获取到上层作用域中的变量的~~
lambda为了解决上述问题,引l入了“变量捕获”语法通过变量捕获,获取到外层作用域中的变量

connect(button, &QPushButton::clicked, this, [button](){
	qDebug() << "lambda 被执行了!";
	button->move(300, 300); 
});

![[Pasted image 20250418094151.png]]

connect(button, &QPushButton::clicked, this, [button, this](){
	qDebug() << "lambda 被执行了!";
	button->move(300, 300);
	this->move(100, 100);
});

![[Pasted image 20250418094850.png]]

写作[=]
这个写法的含义就是把上层作用域中所有的变量名都给捕获进来

connect(button, &QPushButton::clicked, this, [=](){
	qDebug() << "lambda 被执行了!";
	button->move(300, 300);
	this->move(100, 100);
});

后续如果我们对应的槽函数比较简单,而且是一次性使用的.就经常会写作这种lambda的形式
另外也要确认捕获到lambda内部的变量是有意义的
回调函数执行时机是不确定的(用户啥时候点击按钮不知道的)无论何时用户点击了按钮,捕获到的变量都能正确使用
![[Pasted image 20250418100015.png]]

由于此处的button是new出来的变量,生命周期跟随整个窗口(挂到对象树上,窗口关闭才会释放)
这个东西就可以在后面随时使用了
类似的this指向的对象widget

这个变量是在main函数结束销毁 main结束,说明进程结束了,
只要进程不结束,widget就可用.this也就可用了,
lambda除了可以按照值的方式来捕获变量[=]还可以按照引l用的方式来捕获[&](Qt中很少这么写)捕获到的变量一般就是各种控件的指针. 指针变量按照值传递或者引用来传递,都无所谓
如果按引用,还得更关注这个引用的变量本身的生命周期

lambda语法是C++11中引l入的
对于Qt5及其更高版本,默认就是按照C++11来编译的
如果使用Qt4或者更老的版本,就需要手动在.pro文件中加上C++11的编译选项

2、函数参数 ( )

(params)表⽰Lambda函数对象接收的参数,类似于函数定义中的⼩括号表⽰函数接收的参数类型和个数。参数可以通过按值(如:(int a,int b))和按引⽤(如:(int &a,int &b))两种⽅式进⾏传递。函数参数部分可以省略,省略后相当于⽆参的函数
![[Pasted image 20250418110809.png]]

3、选项Opt

Opt部分是可选项,最常⽤的是 mutable声明 ,这部分可以省略。
Lambda表达式外部的局部变量通过值传递进来时,其默认是const,所以不能修改这个局部变量的拷⻉,加上mutable就可以修改。
![[Pasted image 20250418110934.png]]

4、Lambda表达式的返回值类型 ->

可以指定Lambda表达式返回值类型;如果不指定返回值类型,则编译器会根据代码实现为函数推导⼀个返回类型;如果没有返回值,则可忽略此部分。
![[Pasted image 20250418111008.png]]

5、Lambda表达式的函数体 { }

Lambda表达式的函数体部分与普通函数体⼀致。⽤ { } 标识函数的实现,不能省略,但函数体可以为空。

信号与槽的优缺点

优点:松散耦合

信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了⾃⼰,Qt的信号槽机制保证了信号与槽函数的调⽤。⽀持信号槽机制的类或者⽗类必须继承于QObject类。

缺点:效率较低

与回调函数相⽐,信号和槽稍微慢⼀些,因为它们提供了更⾼的灵活性,尽管在实际应⽤程序中差别不⼤。通过信号调⽤的槽函数⽐直接调⽤的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调⽤速度对性能要求不是⾮常⾼的场景是可以忽略的,是可以满⾜绝⼤部分场景

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

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

相关文章

单元测试的一般步骤

Qt Test Qt Test 是 Qt 开发人员发布的一个单元测试框架&#xff0c;用于测试基于 Qt 框架的应用程序或库。它提供了单元测试框架中常见的所有功能以及用于测试图形用户界面的扩展。 1.自动化测试包络ui测试>接口测试>单元测试&#xff1b;现问如何使用Qt进行单元测试&…

UE5 渲染视频

文章目录 概述插件开始渲染渲染透明背景的视频 概述 渲染视频需要使用关卡序列 渲染原理就是将一个关卡序列渲染为序列帧 序列帧放到AE里会自动变成视频 UE版本是5.4.4 插件 首先开启新的渲染插件&#xff0c;否则会自动使用旧的渲染插件 插件里搜Render&#xff0c;开启这…

pycharm无法识别到本地python的conda环境解决方法

问题一 现象描述&#xff1a; 本地已经安装了conda&#xff0c;但在pycharm中选择conda环境却识别不到&#xff0c; 解决方法&#xff1a;手动输入conda path&#xff0c;点击R eload environments基本就能修复&#xff0c;比如我的路径如下 /Users/test/conda/miniconda3/b…

LFM调制信号分类与检测识别

LFM调制信号分类与检测识别 LFM调制信号分类识别AlexNet网络识别InceptionV3、ResNet-18、ResNet-50网络识别 LFM调制信号检测识别 LFM调制信号分类识别 支持识别LFM信号、间歇采样干扰(ISRJ)、灵巧噪声干扰(SNJ)、扫频干扰(SJ)、瞄准干扰(AJ)、阻塞干扰(BJ)、密集假目标干扰(…

头歌实训之连接查询

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习C语言的相关知识。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更…

常见的服务器硬盘接口

常见的服务器硬盘接口有SATA、SAS、M.2、U.2 一、SATA接口 SATA&#xff08;Serial Advanced Technology Attachment&#xff09;是广泛应用于存储设备的串行接口标准&#xff0c;在服务器中主要用于连接大容量机械硬盘&#xff08;HDD&#xff09;或经济型固态硬盘&#xff…

SpringBoot编写单元测试

pom.xml引入单元测试的坐标 <!--单元测试坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>编写单元测试类 测试类…

目标分割模型优化自身参数都是梯度下降算法吗?

在计算机视觉的深度学习任务中&#xff0c;诸如 CNN、FCN、U-Net、DeepLab 系列模型已成为图像分类与图像分割任务的核心架构。它们在网络结构和任务上有所差异&#xff0c;但是否共享同一种优化机制&#xff1f;是否都使用梯度下降&#xff1f;优化过程中又有什么本质区别&…

基于springboot的商城

1 项目使用技术 后端框架&#xff1a;SpringBoot 数据库&#xff1a;MySQL 开发工具&#xff1a;IDEA 2 项目功能模块 商城功能包含前台和后台。 &#xff08;1&#xff09;前台主要包含&#xff1a;用户注册登录模块、首页模块、搜索模块、商品详情、购物车、提交订单、…

MATLAB 控制系统设计与仿真 - 37

范数鲁棒控制器的设计 鲁棒控制器的设计 根据双端子状态方程对象模型结构&#xff0c;控制器设计的目标是找到一个控制器K(s),它能保证闭环系统的范数限制在一个给定的小整数下&#xff0c;即 这时控制器的状态方程为&#xff1a; 其中X与Y分别为下面两个代数Riccati方程的解…

英码科技与泊川软件,携手加速AI与嵌入式系统融合创新

2025年4月15日&#xff0c;广州英码信息科技有限公司&#xff08;以下简称“英码科技”&#xff09;与广州泊川软件技术有限公司&#xff08;以下简称“泊川软件”&#xff09; 正式签署战略合作框架协议。此次合作将充分发挥双方在AI计算硬件与嵌入式操作系统领域的技术优势&a…

电脑 访问 github提示 找不到网页,处理方案

1、找到 本机的 host文件 例如 windows 的 一般在 C:\Windows\System32\drivers\etc\hosts 用管理员身份打开 hosts 文件 如果文件中没有 github的配置&#xff0c;需要自己手动添加上去&#xff1b; 如果有&#xff0c;则需要 检查 github.com 与 github.global.ssl.fastly.…

Linux学习——了解和熟悉Linux系统的远程终端登录

Linux学习——了解和熟悉Linux系统的远程终端登录 一.配置Ubuntu系统的网络和用户 1、设置虚拟机网络为桥接模式 打开VMWare&#xff0c;选择编辑虚拟机设置&#xff0c;在网络适配器设置中&#xff0c;选择“桥接模式”&#xff0c;保存设置并启动Ubuntu。 2、配置Ubuntu的…

AI 中的 CoT 是什么?一文详解思维链

文章目录 CoT 的组成CoT 的作用CoT 的推理结构变体CoT 的特点CoT 的适用场景总结 在人工智能领域&#xff0c;尤其是自然语言处理和机器学习中&#xff0c;有一种名为思维链&#xff08;Chain of Thought&#xff0c;CoT&#xff09;的技术&#xff0c;它正逐渐改变着我们对 AI…

【OC】AVPlayerLayer的学习

文章目录 前言关于AVPlayer概念及作用具体方法及使用 关于AVPlayerLayer基本概念及作用具体用法 AVPlayer与AVPlayerLayer结合实现视频播放应用使用本地视频使用网络请求申请视频数据 总结 前言 在编写类视频软件项目时&#xff0c;涉及到视频播放的问题&#xff0c;我们需要给…

C++笔记-list

list即是我们之前学的链表&#xff0c;这篇主要还是讲解list的底层实现&#xff0c;前面会讲一些list区别于前面string和vector的一些接口以及它们的注意事项。 一.list的基本使用 和之前的string&#xff0c;vector一样&#xff0c;有很多之前见过的一些接口&#xff0c;经过…

open webui 介绍 是一个可扩展、功能丰富且用户友好的本地部署 AI 平台,支持完全离线运行。

AI MCP 系列 AgentGPT-01-入门介绍 Browser-use 是连接你的AI代理与浏览器的最简单方式 AI MCP(大模型上下文)-01-入门介绍 AI MCP(大模型上下文)-02-awesome-mcp-servers 精选的 MCP 服务器 AI MCP(大模型上下文)-03-open webui 介绍 是一个可扩展、功能丰富且用户友好的…

极狐GitLab 登录限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 登录限制 (BASIC SELF) 您可以使用登录限制自定义 Web 界面以及基于 HTTP(S) 的 Git 的身份验证限制。 设置 要访问登录限…

嵌入式exfat-nofuse文件系统移植和使用

exfat-nofuse 是一款专为linux ARM平台设计的开源项目,它提供了一个非FUSE机制的内核级驱动,用于在Linux系统上无缝地读写exFAT和VFAT文件系统。此项目由Dorimanx维护,采用C语言编写&#xff0c;兼容GPL-2.0许可证。它避开了FUSE&#xff08;用户空间文件系统&#xff09;的使用…

再来一篇,Linux中的软件管理

Linux中软件包的类型 在Linux系统中&#xff0c;软件包有多种不同的格式和类型&#xff0c;主要包括以下几种&#xff1a; DEB (Debian软件包)&#xff08;此软件包不适用于RHEL8 系统&#xff09;&#xff1a; 适用于 Debian 及其衍生版本&#xff08;如Ubuntu等&…