当前位置: 首页 > news >正文

二、信号与槽

目录

  • 第二章 信号与槽
    • 1. 信号和槽概述
    • 2. 信号和槽的使用
      • 2.1 连接信号和槽
      • 2.2 查看内置信号和槽
      • 2.3 通过 Qt Creator 生成信号槽代码
    • 3. 自定义信号和槽
      • 3.1 基本语法
      • 3.2 带参数的信号和槽
    • 4. 信号与槽的连接方式
      • 4.1 一对一
      • 4.2 一对多
      • 4.3 多对一
    • 5. 信号和槽的其他说明
      • 5.1 信号与槽的断开
      • 5.2 Qt4 版本信号与槽的连接
      • 5.3 使用 Lambda 表达式定义槽函数
      • 5.4 信号与槽的优缺点

第二章 信号与槽

1. 信号和槽概述

在 Qt 中,用户和控件的每次交互都可以看作一个事件。例如点击按钮、关闭窗口、输入内容等,都会触发对应的事件。

事件发生后,Qt 会发出对应的信号;收到信号后执行的响应函数,就是槽函数。信号和槽可以把两个相互独立的对象关联起来,例如把“按钮点击”这个信号和“窗口关闭”这个槽函数连接起来。

信号的本质:信号可以理解为事件发生后的通知。常见事件包括按钮单击、按钮双击、窗口刷新、鼠标移动、鼠标按下、鼠标释放、键盘输入等。

在代码层面,信号的呈现形式就是函数。某个事件发生后,Qt 框架会触发对应的信号函数。信号的发送者通常是某个已经实例化的 Qt 对象。

槽的本质:槽(Slot)就是响应信号的函数。槽函数本质上仍然是 C++ 函数,可以定义在publicprotectedprivate区域,也可以有参数、重载或被直接调用。

说明:信号和槽机制底层可以理解为函数之间的调用关系。例如按钮的点击信号可以用clicked()表示,窗口关闭槽函数可以用close()表示。连接之后,点击按钮就相当于触发clicked(),再由它调用close()

信号函数通常写在signals下,只需要声明,不需要手动实现;槽函数可以写在public slotsprotected slotsprivate slots或普通成员函数区域,需要自己实现。

Qt 会在编译前自动生成信号相关代码,这类机制可以理解为元编程(Meta Programming)。

2. 信号和槽的使用

2.1 连接信号和槽

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

关于 QObject:QObject是 Qt 内置的基础父类,Qt 中很多类都直接或间接继承自QObject

connect()函数原型:

connect(constQObject*sender,constchar*signal,constQObject*receiver,constchar*method,Qt::ConnectionType type=Qt::AutoConnection);

参数说明:

sender:信号的发送者。

signal:发送的信号,也就是信号函数。

receiver:信号的接收者。

method:接收信号后执行的槽函数。

type:连接方式,默认值为Qt::AutoConnection,通常不需要手动设置。

代码示例:在窗口中设置一个按钮,当点击“按钮”时关闭“窗口”。

2.2 查看内置信号和槽

系统自带的信号和槽通常通过 Qt 帮助文档查询。例如要查看按钮的信号,可以在帮助文档中搜索QPushButton,先在Contents中查找signals。如果当前类中没有找到,就继续到父类中查找,例如QPushButton的父类QAbstractButton

这里的clicked()就是要找的信号。槽函数的寻找方式和信号一样,只不过它的关键字是 slot。

2.3 通过 Qt Creator 生成信号槽代码

Qt Creator 可以快速生成信号槽相关代码。

代码示例:在窗口中设置一个按钮,当点击按钮时关闭窗口。

1、新建项目,如下图为新建完成之后所包含的所有文件。注意:创建项目时需要生成 UI 设计文件。

2、双击 widget.ui 文件,进入 UI 设计界面;

3、在 UI 设计窗口中拖入一个"按钮",并且修改"按钮"的名称及字体大小等;

4、可视化生成槽函数;

当单击"转到槽…"之后,出现如下界面:对于按钮来说,当点击时发送的信号是:clicked(),所以此处选择:clicked()

对于普通按钮来说,使用clicked信号即可;clicked(bool)主要用于带状态的按钮,例如复选按钮。

5、自动生成槽函数原型框架。

(1)在widget.h头文件中自动添加槽函数声明;

自动生成槽函数的命名规则:on_XXX_SSS

XXX表示控件的objectName属性,SSS表示对应的信号。例如on_pushButton_clicked()中,pushButton是对象名,clicked是对应的信号。

按照这种命名风格定义的槽函数,会被 Qt 自动连接到对应信号。

实际开发中,除非是 IDE 自动生成代码,否则更推荐显式调用connect()。这样能更清晰地描述信号和槽的连接关系,也能减少因为命名或拼写问题导致连接失效的情况。

(2)在widget.cpp中自动生成槽函数定义。

6、在槽函数定义中添加要实现的功能,实现关闭窗口的效果。

3. 自定义信号和槽

3.1 基本语法

Qt 允许自定义信号函数和槽函数,但书写方式需要遵守固定规则。

自定义信号函数:

1、自定义信号函数必须写在signals下。

2、返回值为void,只需要声明,不需要实现。

3、可以带参数,也可以重载。

自定义槽函数:

1、早期 Qt 版本要求槽函数写在public slots下;较新的 Qt 版本也允许写在普通public作用域中,或者写成普通全局函数。

2、返回值通常为void,需要声明,也需要实现。

3、可以带参数,也可以重载。

发送信号:使用emit关键字发送信号。emit本质上是一个空宏,可写可不写,主要用于提升代码可读性。

示例1:自定义信号和槽。

1、在widget.h中声明自定义信号和槽;

2、在widget.cpp中实现槽函数,并连接信号和槽。注意图中 ① 和 ② 的顺序不能颠倒:需要先建立连接,再发送信号;如果先发送信号,此时槽函数还没有关联,就不会触发响应。

示例2:自定义Teacher信号与Student槽函数。

1、在源文件中新建两个类:一个是Teacher类,一个是Student类。首先选中项目名称,鼠标右键选择 “add new…”。

点击"add new…"之后,出现如下界面:

选择 “choose” 后进入类配置界面。注意:在 Qt 中新建类时,需要选择新类的父类。

如果新类不是窗口或控件,也没有更合适的业务父类,可以选择QObject作为基类。这样新类对象可以配合 Qt 的对象树机制,便于对象释放。

选择“下一步”,进入如下界面:

按照同样的方式添加Student类。添加完成后,项目目录新增文件如下:

在 teacher.h 中声明信号函数:

在 student.h 中声明槽函数:

在 widget.h 中实例化Teacher对象和Student对象;

在 student.cpp 中实现槽函数:

在 widget.cpp 中连接自定义信号和槽;

运行结果如下:

示例3:点击按钮触发自定义信号;

运行结果如下:

3.2 带参数的信号和槽

Qt 的信号和槽支持参数,也支持重载。通常要求信号函数的参数列表和槽函数的参数列表保持匹配。

当信号触发时,信号函数中的实参会传递给槽函数的形参,这样就可以通过信号向槽传递数据。

示例1:重载信号槽。

(1)在widget.h中声明重载的信号函数和槽函数;

(2)在widget.cpp中实现重载槽函数,并连接信号和槽。注意:定义函数指针时,需要指明函数指针的作用域。

(3)执行结果如下图所示:

示例2:信号槽参数列表匹配规则。

1、在widget.h中声明信号和槽函数;

2、在widget.cpp中实现槽函数,并连接信号和槽;

信号的参数个数可以多于槽函数的参数个数,但槽函数的参数个数不能多于信号参数个数。实际开发中,最好让两者参数列表保持一致。

示例3:参数不完全一致时的连接。

1、在widget.h中声明信号和槽函数;

2、在widget.cpp中实现槽函数,并连接信号和槽;

4. 信号与槽的连接方式

4.1 一对一

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

(1)一个信号连接一个槽

示例:一对一连接。

1、在widget.h中声明信号、槽以及信号发射函数;

2、在widget.cpp中实现槽函数、信号发射函数,并连接信号和槽;

(2)一个信号连接另一个信号

示例:在上述示例基础上,在widget.cpp中添加如下代码:

4.2 一对多

一个信号连接多个槽

示例:一个信号连接多个槽。

(1)在widget.h中声明一个信号和三个槽;

(2)在widget.cpp中实现槽函数,并连接信号和槽;

4.3 多对一

多个信号连接一个槽函数

示例:多个信号连接一个槽。

(1)在widget.h中声明两个信号以及一个槽;

(2)在widget.cpp中实现槽函数,并连接信号和槽;

5. 信号和槽的其他说明

5.1 信号与槽的断开

使用disconnect()可以断开信号和槽的连接,基本用法和connect()类似。

示例:

5.2 Qt4 版本信号与槽的连接

Qt4 中的connect()写法比 Qt5 更复杂,需要配合SIGNALSLOT宏使用,而且缺少函数类型检查,代码更容易出错。

示例:Qt4 写法。

(1)在widget.h中声明信号和槽;

(2)在widget.cpp中实现槽函数,并连接信号与槽;

Qt4 写法的特点:

优点:参数直观。

缺点:参数类型不做检测,编译期不容易发现错误。

示例:

5.3 使用 Lambda 表达式定义槽函数

Qt5 提高了信号与槽的灵活性,允许使用任意函数作为槽函数。需要临时定义简单槽函数时,可以使用 Lambda 表达式来简化代码。

Lambda 表达式是 C++11 增加的特性,用于定义匿名函数对象,可以简化槽函数的编写。

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

[capture](params)opt->ret{Function body;};
组成部分说明
capture捕获列表,用于指定外部变量的捕获方式。
params参数列表,类似普通函数的参数。
opt函数选项,常见的是mutable
ret返回值类型,可以显式指定,也可以由编译器推导。
Function body函数体,即 Lambda 表达式具体执行的代码。

1、局部变量引入方式[]

[]是 Lambda 表达式的捕获列表,用来声明外部变量的捕获方式。

捕获方式说明
[]不捕获任何外部局部变量。
[a]以值传递方式捕获变量a
[&b]以引用传递方式捕获变量b
[=]以值传递方式捕获外部所有局部变量,函数体中使用的是副本。
[&]以引用方式捕获外部所有局部变量。
[=, &foo]foo使用引用捕获,其余变量使用值捕获。
[&, foo]foo使用值捕获,其余变量使用引用捕获。
[this]捕获当前对象指针,可以在函数体中访问类的成员函数和成员变量。

说明:使用引用捕获时要注意变量生命周期。如果 Lambda 执行时,被引用捕获的局部变量已经释放,就会产生不可预期的结果。实际开发中更常见的写法是[=](){}

早期 Qt 版本如果要使用 Lambda 表达式,需要在.pro文件中添加:

CONFIG += c++11

Qt 5 及以上版本通常会在新建项目时自动添加相关配置。

示例1:Lambda 表达式的使用

示例2:以[=]方式传递,外部的所有变量在 Lambda 表达式中都可以使用

示例3:以[a]方式传递,在 Lambda 表达式中只能使用传递进来的 a

2、函数参数()

(params)表示 Lambda 函数对象接收的参数,类似于函数定义中的小括号表示函数接收的参数类型和个数。参数可以通过按值(如:(int a,int b))和按引用(如:(int&a,int&b))两种方式进行传递。函数参数部分可以省略,省略后相当于无参的函数。

示例:

3、选项Opt

Opt部分是可选项,最常用的是mutable声明。Lambda 表达式通过值捕获外部局部变量时,默认不能修改这个副本;加上mutable后就可以修改。

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

可以显式指定 Lambda 表达式的返回值类型;如果不指定,编译器会根据函数体自动推导;如果没有返回值,可以省略这一部分。

示例1:

示例2:

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

Lambda 表达式的函数体和普通函数体类似,用{}表示具体实现。函数体不能省略,但可以为空。

示例:

6、使用 Lambda 表达式实现槽函数

示例1:点击按钮关闭窗口;

示例2:当connect()的第三个参数为this时,第四个参数使用 Lambda 表达式时,可以省略this

5.4 信号与槽的优缺点

优点:松散耦合。

信号发送者不需要知道信号会被哪个对象的槽函数接收,槽函数也不需要关心自己关联了哪些信号。Qt 的信号槽机制会负责完成调用。需要注意的是,支持信号槽机制的类或父类必须继承自QObject

缺点:效率略低。

与直接回调相比,信号和槽会稍慢一些,因为它提供了更高的灵活性。额外开销主要来自查找接收对象、遍历连接关系、处理参数以及跨线程排队等过程。不过在大多数 GUI 开发场景中,这点开销通常可以忽略。

对于 GUI 程序来说,用户交互本身通常远慢于函数调用开销。即使信号槽比直接回调慢一些,在大多数界面开发场景中也不会成为性能瓶颈。

http://www.zskr.cn/news/1440070.html

相关文章:

  • AI产品用户流失深度复盘:从技术炫技到可持续服务的鸿沟
  • 基于COT持仓数据构建WTI原油量化交易策略的实战指南
  • 2026年陕西钢结构工程材料源头直供:西安采购商如何锁定稳定供应链? - 优质企业观察收录
  • 软考/期末救急指南:手把手教你根据PDL伪代码快速画出PAD图与N-S图(附常见扣分点)
  • 城市规划师的数据效率工具箱:CAD的hatchgenerateboundary命令与GIS拓扑检查实战
  • 别再为Ubuntu 18.04多网卡上网发愁了!保姆级netplan配置教程,永久设置有线/无线优先级
  • Windows窗口置顶神器:3步解决多任务窗口遮挡难题,工作效率提升80%
  • AI如何重塑DevOps:从智能运维到安全左移的实践指南
  • 苏州晟雅泰电子:海力士芯片物料H54G46CYRBX267N ,在批次21+和25+的区别及在实际应用中的注意事项
  • 西安路虎捷豹维修哪家专业?顺进聚宝名车维修 核心团队深耕行业15年|本地靠谱专修维保避坑攻略 - 宁夏壹山网络
  • 月球着陆器DQN训练实战包:TensorFlow 2.10实现,含训练/测试/视频录制与预训练模型
  • 2026宁波黄金回收优选|三十年老店收的顶,价透秤准变现无忧 - 奢侈品回收测评
  • 深度解析:UABEA跨平台Unity资源处理工具的技术架构与实践
  • 2026南宁包包回收实地深度测评,添价收包包回收实测出圈 - 薛定谔的梨花猫
  • 哔哩下载姬:5步掌握B站视频下载的终极解决方案
  • TC264智能车实战:用逐飞库的PIT定时器和编码器实现精准速度闭环控制
  • 宝宝起名哪里好?五维命名法给出专业解决方案 - 速递信息
  • Cobimetinib考比替尼联合维莫非尼治疗BRAF V600E突变黑色素瘤效果
  • 2026 安徽蚌埠市(全区域服务)本地人必选彩钢瓦金属屋面防水防腐公司避坑指南 TOP5 推荐 - 本地便民网
  • ⑯ AI教育与培训:知识变现的智能化升级#
  • Arm Ethos-U85 NPU架构与指令集深度解析
  • 半年 AI Agent 开发踩了 7 个坑,每一个都是代码换来的教训
  • 抖音视频怎么在线解析提取无水印?2026全场景无损操作方法汇总 - 科技热点发布
  • AI赋能小企业社交媒体营销:从数据洞察到智能创作的闭环实践
  • 绿色推荐系统:能耗挑战与优化策略
  • Arduino串口数据老丢包?手把手教你搞定缓冲区与延时,附赠一个指令解析框架
  • OpenAI Whisper模型实战指南:从核心原理到部署优化
  • 3分钟快速上手:Carrot浏览器扩展 - Codeforces评分预测的终极指南
  • AI写代码快了一倍,代码质量却烂了——微软Build明天交答卷
  • X光安检模型训练第一步:手把手教你处理OPIXray和HIXray这两个小众数据集