【Qt 学习笔记】详解Qt中的信号和槽


  • 博客主页:Duck Bro 博客主页
  • 系列专栏:Qt 专栏
  • 关注博主,后期持续更新系列文章
  • 如果有错误感谢请大家批评指出,及时修改
  • 感谢大家点赞👍收藏⭐评论✍

详解Qt中的信号与槽

文章编号:Qt 学习笔记 / 12

文章目录

  • 详解Qt中的信号与槽
    • 一、信号和槽的基本概念
      • 1. 什么是信号
        • 1.1 信号本质
        • 1.2 信号举例
      • 2. 什么是槽
        • 2.1 槽的本质
        • 2.2 说明
    • 二、信号和槽如何使用
      • 1. connect函数
        • 1.1 函数原型
        • 1.2 参数说明
        • 1.3 代码示例
      • 2. 如何查看内置信号和槽
        • 2.1 查询信号
        • 2.1 查询槽
      • 3. 使用 Qt Creator 生成信号槽代码(图形化快速生成信号槽代码)
        • 3.1 实现步骤
        • 3.2 实现结果
    • 三、自定义信号和槽
      • 1. 基础语法
        • 1.1 自定义信号函数书写规范
        • 1.2 自定义槽函数书写规范
        • 1.3 发送信号
        • 1.4 示例代码
      • 2. 带参数的信号和槽
        • 2.1 示例代码
    • 四、信号与槽的连接方式
      • 1. 一对一
        • 1.1 信号连接槽
        • 1.2 信号连接信号
      • 2. 一对多
      • 3. 多对一
    • 五、信号与槽的优缺点
      • 1. 优点: 松散耦合
      • 2. 缺点: 效率较低


一、信号和槽的基本概念

QT中的信号和槽是用于实现对象之间的通信的机制。每个对象都可以发出一个信号,其他对象可以通过连接到该信号的槽来接收并处理信号。

通过将信号和槽连接起来,可以实现对象之间的交互和通信。一个对象的信号可以连接到其他对象的槽,也可以将多个信号连接到同一个槽上。

Qt中可以使用connect函数,把一个信号和一个槽关联起来,后续只要信号触发,Qt就会自动执行槽函数。

在这里插入图片描述

信号源:由哪个控件发出信号
信号类型:用户进行不同的操作,就可能触发不同的信号
信号处理方式:槽(slot)== 函数

QT中的信号和槽是通过使用QObject类的特性来实现的,需要使用宏来声明信号和槽,并使用信号和槽的宏来进行连接。QT提供了一个QMetaObject系统来管理信号和槽的连接和调用。

使用信号和槽机制可以使代码更加灵活,模块化和可重用。它使得对象之间的交互变得简单而直观,并允许通过连接和断开连接来动态地改变交互方式。

在这里插入图片描述

1. 什么是信号

信号可以被认为是一个事件,当某些条件发生时,对象会发出一个信号。例如,鼠标点击、键盘输入或者是对象的状态改变都可以作为信号。

1.1 信号本质

信号是由于用户对窗⼝或控件进行了某些操作,导致窗⼝或控件产⽣了某个特定事件,这时 Qt 对
应的窗⼝类会发出某个信号,以此对⽤⼾的操作做出反应。

1.2 信号举例

信号的本质就是事件

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

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

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

2. 什么是槽

槽是接收信号的函数,当一个信号被发出时,连接到该信号的槽会被调用。槽可以执行任意代码,包括更新界面、处理数据等。

2.1 槽的本质

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

2.2 说明

(1)信号和槽机制底层是通过函数间的相互调⽤实现的。每个信号都可以⽤函数来表⽰,称为信号函数;每个槽也可以用函数表示,称为槽函数。

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

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

  • 信号函数⽤ signals 关键字修饰,槽函数⽤ public slots、protected slots 或者 private slots 修饰。signalsslots 是 Qt 在 C++ 的基础上扩展的关键字,专⻔⽤来指明信号函数和槽函数;

  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。

在这里插入图片描述


二、信号和槽如何使用

1. connect函数

1.1 函数原型
connect (const QObject * sender,  //sender:信号的发送者const char * signal ,    //signal:发送的信号(信号函数)const QObject * receiver , //receiver:信号的接收者const char * method , 		//method:接收信号的槽函数Qt::ConnectionType type = Qt::AutoConnection )  //目前阶段不用考虑,用的不多//⽤于指定关联⽅式,默认的关联⽅式为 Qt::AutoConnection,通常不需要⼿动设定。
1.2 参数说明

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

1.3 代码示例

代码功能:在窗⼝中设置⼀个按钮,当点击 “按钮” 时关闭 “窗⼝”

使用connect进行信号和槽连接,实现点击按钮 关闭窗口的功能
在这里插入图片描述
示例代码:

#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 -> setText("按钮");    //设置按钮文本button->resize(100,100);     //设置按钮大小   button->move(200,200);       //设置按钮位置//连接信号和槽 实现点击按钮  关闭窗口的功能connect(button,&QPushButton::clicked,this,&Widget::close);  
}Widget::~Widget()
{delete ui;
}

2. 如何查看内置信号和槽

在上述代码中使用到的信号和槽可以在“Qt帮助文档(Assistant)”中查看,下面介绍如何查看Qt内置的信号和槽,下面以查询“QPushButton(按钮)”的信号为例。

2.1 查询信号
  1. 打开Assistant,在索引栏中输入QPushButton
    在这里插入图片描述

  2. 然后可以在 “Contents” 中寻找关键字 signals,如果没有请看下一步
    在这里插入图片描述

  3. 如果没有找到, 继续去⽗类中查找.,点击父类QAbstractButton
    在这里插入图片描述

  4. 在⽗类 QAbstractButton 中继续查找关键字signals
    在这里插入图片描述

  5. 找到信号clicked()
    在这里插入图片描述

2.1 查询槽

槽函数的寻找⽅式和信号⼀样,只不过它的关键字是 slot

3. 使用 Qt Creator 生成信号槽代码(图形化快速生成信号槽代码)

在Qt Creator中可以快速帮助我们⽣成信号槽相关的代码

3.1 实现步骤

代码示例如下:
代码功能:在窗⼝中设置⼀个按钮,当点击 “按钮” 时关闭 “窗⼝”

  1. 新建一个Qt项目,如下图为新建完成之后所包含的所有⽂件;
    在这里插入图片描述

  2. 打开widget.ui文件,进入UI界面
    在这里插入图片描述

  3. 在界面中放置一个按钮,并且修改按钮的大小及名称
    在这里插入图片描述

  4. 右键按钮,选择转到槽,生成槽函数
    在这里插入图片描述

  5. 选择信号clicked(),点击OK
    在这里插入图片描述

  6. 自动生成槽函数原型框架

  • 在 “widget.h” 头⽂件中⾃动添加槽函数的声明
    在这里插入图片描述

说明:
⾃动⽣成槽函数的名称有⼀定的规则。槽函数的命名规则为:on_XXX_SSS,其中:
1. 以 " on " 开头,中间使用下划线连接起来;
2. " XXX " 表示的是对象名(控件的 objectName 属性)。
3. " SSS "表示的是对应的信号。

如:" on_pushButton_clicked() " ,pushButton 代表的是对象名,clicked是对应的信号。

在这里插入图片描述

  • 在 “widget.cpp” 文件中⾃动⽣成槽函数定义.
    在这里插入图片描述
  1. 在槽函数函数定义中添加要实现的功能. 实现关闭窗口的效果.
    在这里插入图片描述
3.2 实现结果

点击按钮窗口关闭。
在这里插入图片描述


三、自定义信号和槽

1. 基础语法

在Qt中,自定义信号和槽可以通过使用signals和slots关键字来定义,在日常项目中用到较少。

在 Qt 中,允许⾃定义信号的发送⽅以及接收⽅,即可以⾃定义信号函数和槽函数。但是对于⾃定义的信号函数和槽函数有⼀定的书写规范

需要注意的是,为了使用自定义信号和槽,类必须包含Q_OBJECT宏,并且需要使用Qt的元对象编译系统(MOC)。这意味着类定义必须位于一个.h文件中,并且在CMake或QMake构建系统中添加适当的语句来使用MOC编译器。

1.1 自定义信号函数书写规范
  • ⾃定义信号函数必须写到 “signals” 下;
  • 返回值为 void,只需要声明,不需要实现;
  • 可以有参数,也可以发⽣重载;
1.2 自定义槽函数书写规范
  • 早期的 Qt 版本要求槽函数必须写到 “public slots” 下,但是现在⾼级版本的 Qt 允许写到类的"public" 作⽤域中或者全局下;
  • 返回值为 void,需要声明,也需要实现;
  • 可以有参数,可以发⽣重载;
1.3 发送信号
  • 使⽤ “emit” 关键字发送信号 。“emit” 是⼀个空的宏。“emit” 其实是可选的,没有什么含义,只是为了提醒开发⼈员。
1.4 示例代码
  1. 在 widget.h 中声明⾃定义的信号和槽
    在这里插入图片描述
    2.在 widget.cpp 中实现槽函数,并且关联信号和槽
    在这里插入图片描述
    示例代码:
//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();//信号声明public:void MySlots();//槽函数声明private slots:void on_pushButton_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this,&Widget::MySignal,this,&Widget::MySlots);//emit MySignal();
}Widget::~Widget()
{delete ui;
}void Widget::MySlots()
{this->setWindowTitle("修改窗口1");
}void Widget::on_pushButton_clicked()
{emit MySignal();
}

2. 带参数的信号和槽

Qt 的信号和槽也可以带有参数, 同时也可以⽀持重载.
信号函数的参数列表要和对应连接的槽函数参数列表⼀致.

此时信号触发, 调⽤到槽函数的时候, 信号函数中的实参就能够被传递到槽函数的形参当中,就起到让信号给槽传参的效果

注意:为了使用自定义信号和槽,类必须包含Q_OBJECT宏,并且需要使用Qt的元对象编译系统(MOC)。这意味着类定义必须位于一个.h文件中,并且在CMake或QMake构建系统中添加适当的语句来使用MOC编译器。

2.1 示例代码

自定义信号和槽,参数必须一致,主要要求类型一致;个数如果不一致也可以,要求信号参数个数比槽参数个数多要多

注意:一个槽函数, 有可能会绑定多个信号,如果我们严格要求参数个数一致,就意味着信号绑定到槽的要求就变高了换而言之,当下这样的规则,就允许信号和槽之间的绑定更灵活了更多的信号可以绑定到这个槽函数上了

在这里插入图片描述
在这里插入图片描述

//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 MySignals(const QString& text1,const QString& text2);        //此处text可以省略public:void handleMySignal(const QString& text);   //此处text可以省略private slots:void on_pushButton_clicked();          //UI自动生成的槽函数void on_pushButton_2_clicked();        //UI自动生成的槽函数private:Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this,&Widget::MySignals,this,&Widget::handleMySignal);}Widget::~Widget()
{delete ui;
}void Widget::handleMySignal(const QString& text)
{qDebug()<<text;this->setWindowTitle(text);
}void Widget::on_pushButton_clicked()
{emit MySignals("按钮1修改参数","");
}void Widget::on_pushButton_2_clicked()
{emit MySignals("按钮2修改参数","");
}

四、信号与槽的连接方式

1. 一对一

分为两种形式:⼀个信号连接⼀个槽 和 ⼀个信号连接⼀个信号。

1.1 信号连接槽

在这里插入图片描述

  • 在 “widget.h” 中声明信号和槽
    在这里插入图片描述
  • 在 “widget.cpp” 中实现槽函数,信号发射函数以及连接信号和槽
    在这里插入图片描述
1.2 信号连接信号

在这里插入图片描述

  • 在 “widget.cpp” ⽂件中添加如下代码:
    在这里插入图片描述
    在这里插入图片描述

2. 一对多

在这里插入图片描述

  • 在 “widget.h” 头⽂件中声明⼀个信号和三个槽
    在这里插入图片描述
  • 在 “widget.cpp” ⽂件中实现槽函数以及连接信号和槽;
    在这里插入图片描述

3. 多对一

在这里插入图片描述

  • 在 “widget.h” 头⽂件中声明两个信号以及⼀个槽
    在这里插入图片描述

  • 在 “widget.cpp” ⽂件中实现槽函数以及连接信号和槽
    在这里插入图片描述


五、信号与槽的优缺点

1. 优点: 松散耦合

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

2. 缺点: 效率较低

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


在这里插入图片描述

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

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

相关文章

红黑树的平衡之道:深入解析右旋操作的原理与实践

红黑树的平衡之道&#xff1a;深入解析右旋操作的原理与实践 一、 红黑树旋转的背景二、右旋&#xff08;RIGHT-ROTATE&#xff09;的原理三、右旋&#xff08;RIGHT-ROTATE&#xff09;的算法步骤四、右旋&#xff08;RIGHT-ROTATE&#xff09;的伪代码五、右旋&#xff08;RI…

ctf_show笔记篇(web入门---jwt)

目录 jwt简介 web345&#xff1a; web346&#xff1a; web347&#xff1a; web348: web349&#xff1a; web350&#xff1a; jwt简介 JSON Web Token&#xff08;JWT&#xff09;通常由三部分组成 Header&#xff08;头部&#xff09;&#xff1a;包含了两部分信息&…

蓝桥杯备考3

P8196 [传智杯 #4 决赛] 三元组 题目描述 给定一个长度为 n 的数列 a&#xff0c;对于一个有序整数三元组 (i,j,k)&#xff0c;若其满足 1≤i≤j≤k≤n 并且&#xff0c;则我们称这个三元组是「传智的」。 现在请你计算&#xff0c;有多少有序整数三元组是传智的。 输入格式…

LRU的原理与实现(java)

介绍 LRU的英文全称为Least Recently Used&#xff0c;即最近最少使用。它是一种内存数据淘汰算法&#xff0c;当添加想要添加数据而内存不足时&#xff0c;它会优先将最近一段时间内使用最少的数据淘汰掉&#xff0c;再将数据添加进来。 原理 LRU的原理在介绍中就已经基本说…

机器学习模型——逻辑回归

https://blog.csdn.net/qq_41682922/article/details/85013008 https://blog.csdn.net/guoziqing506/article/details/81328402 https://www.cnblogs.com/cymx66688/p/11363163.html 参数详解 逻辑回归的引出&#xff1a; 数据线性可分可以使用线性分类器&#xff0c;如果…

蓝桥真题--路径之谜DFS解法

路径之谜 思路 前置知识&#xff1a;深度搜索模板搜索所有可以找的路径&#xff0c;将走过的靶子减去一走到最后一个格子的时候&#xff0c;直接去判断所有的靶子只有除最后一个位置的靶子&#xff0c;其余靶子都归零的时候&#xff0c;判断一个最后一个位置横坐标和纵坐标的靶…

尚硅谷html5+css3(1)

1.基本标签&#xff1a; <h1>最大的标题字号 <h2>二号标题字号 <p>换行 2.根标签<html> 包括<head>和<body> <html><head><title>title</title><body>body</body></head> </html> 3…

MATLAB - 用命令行设计 MPC 控制器

系列文章目录 前言 本例演示如何通过命令行创建和测试模型预测控制器。 一、定义工厂模型 本示例使用《使用 MPC Designer 设计控制器》中描述的工厂模型。创建工厂的状态空间模型&#xff0c;并设置一些可选的模型属性&#xff0c;如输入、状态和输出变量的名称和单位。 % co…

正确使用@Resource

目录 1 怎么使用Resource&#xff1f;1.0 实验环境1.1 通过字段注入依赖1.2 bean property setter methods &#xff08;setter方法&#xff09; 2 打破岁月静好&#xff08;Resource takes a name attribute&#xff09;2.1 结论2.2 那我不指定呢&#xff1f;【结论&#xff1…

Seata(分布式事务集成测试和总结)

文章目录 1.集成测试1.集成测试正常下单1.步骤2.浏览器访问 http://localhost:10008/order/save?userId666&productId1&nums1&money1003.注意事项和细节 2.集成测试模拟异常1.步骤1.com/sun/springcloud/controller/StorageController.java 休眠12s&#xff0c;模…

自动驾驶执行层 - 线控底盘基础原理(非常详细)

自动驾驶执行层 - 线控底盘基础原理(非常详细) 附赠自动驾驶学习资料和量产经验&#xff1a;链接 1. 前言 1.1 线控的对象 在自动驾驶行业所谓的“感知-定位-决策-执行”的过程中&#xff0c;在末端的执行层&#xff0c;车辆需要自主执行决策层所给出的指令&#xff0c;具体…

leetcode(HOT100)——链表篇

1、相交链表 本题思路就是定义两指针&#xff0c;指向两链表的同一起跑线&#xff0c;然后共同往前走&#xff0c;边走边判断两链表的节点是否相等&#xff0c; 代码如下&#xff1a; /*** Definition for singly-linked list.* public class ListNode {* int val;* L…

Android14应用启动流程(源码+Trace)

1.简介 应用启动过程快的都不需要一秒钟&#xff0c;但这整个过程的执行是比较复杂的&#xff0c;无论是对手机厂商、应用开发来说启动速度也是核心用户体验指标之一&#xff0c;本文采用Android14源码与perfetto工具进行解析。 源码参考地址&#xff1a;Search trace分析工…

2024.4.5|牛客小白月赛90

2024.4.5|牛客小白月赛90 A.小A的文化节 B.小A的游戏 C.小A的数字 D.小A的线段&#xff08;easy version&#xff09; E.小A的任务 F.小A的线段&#xff08;hard version&#xff09; 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c…

[报错解决]源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示。

目录 报错信息解决办法 spring整合mvc时&#xff0c;遇到的404报错&#xff0c;梳理mvc知识供参考供 报错信息 解决办法 Controller RequestMapping("user") public class UserController {//spring整合webmvc// 请求地址 http://localhost:7070/user/quickRequest…

数据字典

文章目录 一、需求分析二、表设计&#xff08;两张表&#xff09;三、功能实现3.1 数据字典功能3.1.1 列表功能3.1.2 新增数据字典3.1.3 编辑数据字典 3.2 数据字典明细3.2.1 列表功能3.2.2 新增字典明细3.2.3 编辑字典明细 3.3 客户管理功能3.3.1 列表功能3.3.2 新增用户3.3.3…

Golang | Leetcode Golang题解之第11题盛最多水的容器

题目&#xff1a; 题解&#xff1a; func maxArea(height []int) int {res : 0L : 0R : len(height) - 1for L < R {tmp : math.Min(float64(height[L]), float64(height[R]))res int(math.Max(float64(res), tmp * float64((R - L))))if height[L] < height[R] {L} el…

【环境变量】基本概念理解 | 查看环境变量echo | PATH的应用和修改

目录 前言 基本概念&理解 注意的点 查看环境变量方法 PATH环境变量 PTAH应用系统指令 PTAH应用用户程序 命令行的修改&#xff08;内存级&#xff09; 配置文件的修改 windows环境变量 大家天天开心&#x1f642; bash进程的流程。环境变量在系统指令和用户…

Linux网卡IP地址配置错误的影响

在Linux系统中&#xff0c;网络配置是保持系统顺畅运行的关键一环。正确配置网卡的IP地址对于确保网络通信的准确性和效率至关重要。然而&#xff0c;如果在这个过程中发生错误&#xff0c;可能会带来一系列问题。让我们一起探讨一下&#xff0c;如果Linux网卡的IP地址配置错误…

Python+Yolov8框选位置目标识别人数统计计数

程序示例精选 PythonYolov8框选位置目标识别人数统计计数 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonYolov8框选位置目标识别人数统计计数》编写代码&#xff0c;代码整洁&#…