[Qt的学习日常]--信号和槽

前言

作者:小蜗牛向前冲

名言:我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正

本期学习:什么是信号和槽,自定义槽函数和信号函数,信号和槽的传参,断开,对lambda表达式的使用

目录

一、信号和槽

1、信号和槽的理解

2、信号的本质 

3、槽的本质

二、信号和槽的使用 

1、 connect()函数

2、自定义槽函数二种实现方法

3、自定义信号

4、信号和槽的带参传递 

三、 信号和槽的其他说明

1、信号与槽的断开

2、使⽤ Lambda 表达式定义槽函数

3、信号与槽的优缺点


一、信号和槽

1、信号和槽的理解

在Qt中用户和控件的每一次交互都称为一个事件,⽐如 "⽤户点击按钮" 是⼀个事件,"⽤户关闭窗⼝" 也是⼀个事件。每个事件都回发送一个信号,当用户点击按钮,就会发送按钮被点击的信号。

Qt中所有的控件都具备接收信号的能力,一个控件可以接受多种信号,对每种信号都会做出相应的响应动作。例如,按钮所在的窗⼝接收到 "按钮被点击" 的信号后,会做 出 "关闭⾃⼰" 的响应动作;在Qt中,对信号做出响应动作就称为槽。

信号和槽是 Qt 特有的消息传输机制,它能将相互独⽴的控件关联起来。⽐如,"按钮" 和 "窗⼝" 本⾝是两个独⽴的控件,点击 "按钮" 并不会对 "窗⼝" 造成任何影响。通过信号和槽机制,可以将 "按 钮" 和 "窗⼝" 关联起来,实现 "点击按钮会使窗⼝关闭" 的效果。

2、信号的本质 

信号的本质就是事件。

如:按钮单击、双击 • 窗⼝刷新 • ⿏标移动、⿏标按下、⿏标释放 • 键盘输⼊

 信号的呈现形式就是函数, 也就是说某个事件产⽣了, Qt 框架就会调⽤某个对应的信号函数, 通 知使⽤者。

3、槽的本质

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

注意:

(1)信号和槽机制底层是通过函数间的相互

调⽤实现的每个信号都可以⽤函数来表⽰,称为信号函数;每个槽也可以⽤函数表⽰,称为槽函数

例如: "按钮被按下" 这个信号可以⽤ clicked() 函数表 ⽰,"窗⼝关闭" 这个槽可以⽤ close() 函数表⽰,假如使⽤信号和槽机制实现:"点击按钮会关闭窗⼝" 的功能,其实就是 clicked() 函数调⽤ close() 函数的效果。

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

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

二、信号和槽的使用 

1、 connect()函数

在 Qt 中,QObject 类提供了⼀个静态成员函数 connect() ,该函数专⻔⽤来关联指定的信号函数和槽函数。

connect() 函数原型:

​
connect(const QObject* sender,//信号的发送者(哪个控件)const char* signal,//发送信息的函数const QObject* receiver,//信号的接受者const char* method,//接受信号的槽函数Qt::ConnectionType type = Qt::AutoConnection);
//type: ⽤于指定关联⽅式,默认的关联⽅式为 Qt::AutoConnection,通常不需要⼿动设定。

代码⽰例: 在窗⼝中设置⼀个按钮,当点击 "按钮" 时关闭 "窗⼝" .:

大家看到这里可能会有疑惑,为什么我们传递的参数类型没有疑惑是对的, 我们明明是要给第一个参数(信号的发送者)传给QObject类的,怎么就变成了传QPushButton类呢?

这是因为在QT中QObject是所有类继承的基类,通过层层的继承关系,QPushButton也继承了QObject,根据继承关系这里也可以相当与QObject使用,这里我们理解了参数1和参数3.

那为什么参数2和参数4明明传递的是一个函数指针,怎么能和char*类型的指针匹配。

其实最初的Qt为了能给信号参数和槽参数传递参数,是要给这二个参数搭配二个厷

SIGBNAL和SLOT厷。

connect(but,SIGNAL(&QPushButton::licked),this,SLOT(&Widget::close));

 但是从Qt5开始就对是上面的内容进行了简单化,给connect提供了重载版本,其实参数2和参数4变成了泛形编程,就可以允许传递任意类型的函数指针。

QMetaObject::Connection QObject::connect(
const QObject *sender,
PointerToMemberFunction signal,
const QObject *receiver, 
PointerToMemberFunction slot,
Qt::ConnectionType type = Qt::AutoConnection);

2、自定义槽函数二种实现方法

基本语法:

1. 包含Q_OBJECT宏

在你的类定义中,必须包含 Q_OBJECT 宏。这是因为 Q_OBJECT 宏允许类使用Qt的元对象系统,包括信号和槽的机制。

2. 类继承

确保你的类继承自 QObject 或其子类(如 QWidgetQMainWindow 等),这样类才能支持信号和槽。

3. 声明槽函数

在类声明中,槽函数可以在 public slots:protected slots:private slots: 部分声明,这取决于你希望槽函数的访问级别。

4. 连接信号和槽

使用 QObject::connect 方法将信号连接到槽。

这里我们要完成在窗⼝中设置⼀个按钮,当点击 "按钮" 时改变窗口的名称"。

方法1:代码实现:

这里我们要在widget.h头文件中,对槽函数进行声明

 在widget.cpp中进行对函数的编写,这里我们让如果开关被按下就对窗口进行显示更改为“按钮已经被按下"。

方法2: 通过图形化界面

 这里会自动帮我们生成一个函数

 函数名的说明

 这里我们发送,我们并没有借助connect函数将信号和槽链接起来,但是其实Qt会通过函数名字自动连接。

3、自定义信号

其实Qt中内置的信号,基本就够覆盖用户的所以操作了。我们知道,信号的本质其实就是函数,也就是说如果我能要进行自定义信号,就需要自定义信号函数

在widget.h中,我们完成了对信号的定义

 在widget.cpp中调用connect完成对信号和槽的连接

4、信号和槽的带参传递 

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

在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();signals:void MySignal(const QString& text);public:void MYStols(const QString& text);private slots:void on_pushButton_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(this,&Widget::MySignal,this,&Widget::MYStols);
}Widget::~Widget()
{delete ui;
}void Widget::MYStols(const QString & text)
{this->setWindowTitle(text);
}void Widget::on_pushButton_clicked()
{//发出自定义的信号//这里我们就可以通过信号,把我们想要传递的参数给槽的回调函数emit MySignal("信号发送");}

点击允许程序,在点击按钮输出就可以改变窗口名称 

对于这种带参传递的作用最大的好处就是可以进行进行代码复用,因为信号和槽的关系,可以是一对一,一对多的,多对多关系。也就是说,我们可以有多个信号,但我点击不同信号的时候,在同一槽中处理(回调函数),只是不同信号,传递给槽函数的参数不同,从而达到代码复用。

是我们要注意:信号传递给槽函数的参数,可以多但是不能少

多了就会用槽能接受几个就用几个但是少了一定是会报错误的。

三、 信号和槽的其他说明

1、信号与槽的断开

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

在 "widget.h" 头⽂件中声明重载的信号函数以及重载的槽函数

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

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

 

#include "widget.h"
#include "ui_widget.h"
#include"QDebug"
#include"QPushButton"
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(ui->pushButton,&QPushButton::clicked,this,&Widget::MySltos1);
}Widget::~Widget()
{delete ui;
}void Widget::MySltos1()
{this->setWindowTitle("修改窗口1");qDebug()<<"MySltos1修改";
}void Widget::MySltos2()
{this->setWindowTitle("修改窗口2");qDebug()<<"MySltos2修改";
}void Widget::on_pushButton_2_clicked()
{//首先断开连接disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::MySltos1);//然后重新绑定连接connect(ui->pushButton,&QPushButton::clicked,this,&Widget::MySltos2);
}

这里我们允许程序,点击修改,首先是窗口名称变成 修改窗口1

当我们切换修改就变为,在点击修改就变为修改窗口2 。

注意如果没有用disconnect 断开连接,在点击切换后,会调用二个槽函数

2、使⽤ Lambda 表达式定义槽函数

但如果想⽅便的编写槽函数,⽐如在编写函数时连函数名都不想定义,则可以通过 Lambda表达式 来 达到这个⽬的。 Lambda表达式 是 C++11 增加的特性。C++11 中的 Lambda表达式 ⽤于定义并创建匿名的函数对 象,以简化编程⼯作。

对于lambda表达式这里进行简单介绍:

Lambda表达式 的语法格式如下:

[ capture ] ( params ) opt -> ret { Function body; 
};
  • capture 捕获列表
  • params 参数表
  • opt 函数选项
  • ret 返回值类型
  • Function body 函数

 局部变量引⼊⽅式 [ ]

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

 

运用实例:

3、信号与槽的优缺点

优点: 松散耦合

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

缺点: 效率较低

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

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

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

相关文章

【JAVA】一文掌握Java并发编程

Java 开发中&#xff0c;并发编程属于相当重要的一个知识点&#xff0c;可以说&#xff0c;Java 的并发能力&#xff0c;是成就今日 Java 地位的因素之一。Java 的并发编程由浅入深实质上是包含 Java&#xff08;API&#xff09;层、JVM&#xff08;虚拟机&#xff09;层、内核…

[Linux][网络][网络编程套接字][一][预备知识][套接字地址结构]详细讲解

目录 0.预备知识1.理解源IP地址和目的IP地址2.理解源MAC地址和目的MAC地址3.端口号4.理解端口号和进程ID5.理解源端口号和目的端口号6.通过IP地址、端口号、协议号进行通信识别7.认识TCP协议和UDP协议8.网络字节序 1.套接字地址结构(sockaddr) 0.预备知识 1.理解源IP地址和目的…

redisson分布式锁的单机版应用

package com.redis;/*** author linn* date 2024年04月23日 15:31*/ import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.…

从 0 到 1 创建、测试并发布属于自己的 Go 开源库

作者&#xff1a;陈明勇 个人网站&#xff1a;https://chenmingyong.cn 文章持续更新&#xff0c;如果本文能让您有所收获&#xff0c;欢迎点赞收藏加关注本号。 微信阅读可搜《程序员陈明勇》。 这篇文章已被收录于 GitHub https://github.com/chenmingyong0423/blog&#xff…

AIGC——什么是人工智能生成内容

人工智能生成内容&#xff08;AIGC&#xff09;是当今数字时代的一个引人注目的前沿技术&#xff0c;它借助深度学习和自然语言处理等技术&#xff0c;使计算机系统具备了生成高质量文本、图像、音频等多媒体内容的能力。AIGC的出现不仅推动了信息技术的发展&#xff0c;也在多…

判断字符串由几个单词组成(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int world 0;int i 0;char c 0;char string[81] { 0 };int num 0;//提示用户&#xff…

C++---重载

1、运算符重载 #include <iostream> using namespace std; class complex { int rel; int vir; public: complex(){} complex(int rel,int vir):rel(rel),vir(vir){} void show() { cout << rel << "" << vir << "i" <&l…

(待更)DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析

前言&#xff1a;还没有整理&#xff0c;后续有时间再整理&#xff0c;目前只是个人思路&#xff0c;文章较乱。 注意路径匹配的“/” 我们的url里面加了“/”&#xff0c;但是用apifox等非浏览器的工具发起请求时没有加“/”&#xff0c;而且还不是get请求&#xff0c;那么这…

31.Gateway网关-跨域问题

跨域 1.域名不同&#xff1a;www.baidu.com和www.taobao.com,www.taobao.org 2.域名相同&#xff0c;端口不同。localhost:8080和localhost:8081 跨域问题 浏览器禁止请求的发起者与服务端发生跨域ajax请求&#xff0c;请求被浏览器拦截的问题。 解决方案 CORS 浏览器询…

0426_C高级4

练习1&#xff1a; 输入一个数字&#xff0c;实现数字逆置&#xff08;不使用字符串截取方式&#xff09; 1 #!/bin/bash2 read -p "输入一个数字&#xff1a;" number3 p$number4 result5 while [ $p -ne 0 ]6 do7 result$((result*10p%10))8 p$((p/10))9 …

input框添加验证(如只允许输入数字)中文输入导致显示问题的解决方案

文章目录 input框添加验证(如只允许输入数字)中文输入导致显示问题的解决方案问题描述解决办法 onCompositionStart与onCompositionEnd input框添加验证(如只允许输入数字)中文输入导致显示问题的解决方案 问题描述 测试环境&#xff1a;react antd input (react的事件与原生…

无人机GB42590接收端 +接收端模组,同时支持2.4G与5.8G双频

严格按照GB42590的协议开发的发射端&#xff0c;通过串口和模块通讯&#xff0c;默认波特率 921600。 http://www.doit.am/深圳四博智联科技有限公司https://shenzhendoit.taobao.com/category-1734422372.htm?spma1z10.1-c-s.0.0.560c74d77eT01G&searchy&catNameGB4…

VC2022 + protobuf

google这是有私心啊&#xff0c;protobuf从某个版本开始&#xff0c;依赖了一个google自己推出的大型组件集&#xff0c;Abseil&#xff0c;有点类似于Boost了&#xff0c;业内用的人&#xff0c;从个人狭窄的圈子来说&#xff0c;应该是不多的&#xff0c;据说google的众贤用的…

【LLMOps】小白详细教程,在Dify中创建并使用自定义工具

文章目录 博客详细讲解视频点击查看高清脑图 1. 搭建天气查询http服务1.1. flask代码1.2. 接口优化方法 2. 生成openapi json schema2.1. 测试接口2.2. 生成openapi schema 3. 在dify中创建自定义工具3.1. 导入schema3.2. 设置工具认证信息3.3. 测试工具 4. 调用工具4.1. Agent…

docker启动的mysql8中文乱码问题和无法输入中文

问题描述&#xff1a; 1.中文显示乱码 2.无法输入中文 中文乱码临时方案&#xff1a; show variables like ‘character%’; SET NAMES utf8mb4; SET CHARACTER SET utf8mb4; 中文乱码永久方案&#xff1a; vim /etc/my.cnf[client] default-character-setutf8mb4[mysql]…

Linux详解:进程创建

文章目录 进程创建fork函数写时拷贝页表fork常规用法fork调用失败的原因 进程创建 fork函数 在linux 中fork函数&#xff0c;它从已经存在的进程中创建一个新的进程&#xff0c;新进程为子进程&#xff0c;而原进程为父进程。 #include<unistd.h> pid_t fork(void);返…

嵌入式学习58-ARM7(字符设备驱动框架led)

知识零碎&#xff1a; kernel 内核 printk 内核打印 cat /proc/devices insmod …

如何通过安全数据传输平台,保护核心数据的安全传输?

在数字化的浪潮中&#xff0c;企业的数据安全传输显得尤为关键。随着网络攻击手段的日益复杂&#xff0c;传统的数据传输方式已不再安全&#xff0c;这就需要我们重视并采取有效的措施&#xff0c;通过安全数据传输平台来保护核心数据。 传统的数据传输面临的主要问题包括&…

【介绍下IDM的实用功能】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【已解决】仅当从 VS 开发人员命令提示符处运行 VS Code 时,cl.exe 生成和调试才可用。

当我们在使用vs code运行.c文件时可能会出现如下报错&#xff1a; 这是因为我们使用的生成和调试方式是cl.exe&#xff0c;我们需要更改到gcc.exe。 解决办法&#xff1a; 将所需运行的文件复制粘贴到新的一个文件夹下&#xff0c;此时再点击运行按钮会弹出如下窗口&#xf…