Qt基础之四:Qt信号与槽机制原理及优缺点

news/2024/5/19 0:34:26 标签: qt, 信号, , signal, slot

信号和插用于对象之间的通信。信号机制是Qt的核心特性,可能也是与其他框架提供的特性最大不同的部分。Qt的元对象系统使信号成为可能。

一.简介

在GUI编程中,当我们改变一个控件,通常希望其他控件被通知到。更一般的,我们希望任意对象之间能够通信。例如,如果我们点击了“关闭”按钮,我们希望窗口的close()函数被调用。
其他工具包使用回调来实现这种通信。回调函数是一个指向函数的指针,所以如果你想要一个处理函数通知你一些事件,你可以将一个指向另一个函数(回调函数)的指针传递给处理函数。处理函数然后在适当的时候调用回调函数。但回调可能不太直观,而且在确保回调参数的类型正确性方面可能会遇到问题。

二.信号

在Qt中,我们有一个回调技术的替代方案:我们使用信号。当特定事件发生时发出信号。Qt的小部件有许多预定义的信号,但是我们总是可以子类化小部件,添加一些自定义信号slots)是响应特定信号signals)而调用的函数。Qt的小部件有许多预定义的,但是通常的做法是子类化控件并自定义函数,这样就可以处理感兴趣的信号
下图为信号的关系图:支持一对多,多对一


1.信号机制是类型安全的
信号机制要求信号的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,信号的参数可以比函数的参数多,此时可以忽略多余参数。由于参数的一致性,所以当使用基于函数指针的语法时,编译器可以帮助我们检测类型是否匹配;基于字符串的信号语法将在运行时检测类型不匹配。
2.信号是松散耦合的
信号是松散耦合的,发出信号的类既不知道也不关心哪个接收信号。Qt的信号机制确保,如果您将信号连接到时隙,将在正确的时间使用信号的参数调用该信号可以采用任意数量的任何类型的参数。它们完全是类型安全的。
从QObject或其子类(例如QWidget)继承的所有类都可以包含信号。当对象的状态发生变化时,它们会发出信号,它不知道或不关心是否有任何在接收它发出的信号,这足以实现信息封装。
可以用于接收信号,但它们也是正常的成员功能。就像一个对象不知道是否有任何其他对象接收到它的信号一样,也不知道它是否有任何信号连接到它。这确保了可以使用Qt创建真正独立的组件。
您可以将任意多个信号连接到一个,并且可以将信号连接到任意多个。甚至可以将一个信号直接连接到另一个信号。(每当发出第一个信号时,这将立即发出第二个信号。)
信号共同构成了强大的组件编程机制。

三.信号signals)

当对象内部状态以对象的客户端或用户感兴趣的某种方式发生变化时—比如点击、鼠标移动等,对象就会发出信号信号signals)都是共有(public)的,可以从任何地方发出,但最好只从该定义信号的类及其子类使用该信号。当信号发出时,通常采用直连方式连接函数,这种连接方式会立即执行函数,就像普通的函数调用一样。此时信号机制完全独立于任何GUI事件循环。一旦所有的都返回,emit语句之后的代码就会执行(同步发送)。当使用队列连接时,情况略有不同;在这种情况下,emit关键字后面的代码将立即继续,将稍后执行(异步发送)。如果多个连接到一个信号,当信号发出时,将按照它们连接的顺序依次执行。信号是由moc(元对象编译器)自动生成的,在build目录中的moc_**文件中。信号永远不能有返回类型(即使用void)。

四.slots)

信号发出时被调用。slot也是C++函数,可以像普通函数一样正常调用,它们唯一的特点是可以连接信号。因为函数是普通的成员函数,所以当直接调用时,它们遵循普通的C++规则。与回调相比,信号稍微慢一些,因为它们提供了更大的灵活性,尽管实际应用程序中的差异并不大。一般来说,emit一个连接到一些信号,大约比直接调用非虚函数慢十倍。原因是在定位连接对象、安全遍历所有连接(即检查在发送过程中后续函数是否被销毁)等所需的开销。虽然十个非虚函数调用可能听起来很多,但它的开销比任何new或delete操作都要小得多。一旦在后续执行需要new或delete的字符串、vector or list等操作,信号开销只占整个函数调用开销的很小一部分。信号机制的简单性和灵活性是非常值得的,用户甚至不会注意到这些开销。
注意,当定义了signals或slots变量的第三方库与基于qt的应用程序一起编译时,可能会导致编译器警告和错误。要解决这个问题,请#undef有问题的预处理器符号

五.在Qt中使用第三方的Signals和Slots

可以将Qt与第三方信号/机制一起使用。你甚至可以在同一项目中使用这两种机制。只需将以下行添加到qmake项目(.pro)文件中。

CONFIG += no_keywords

它告诉Qt不要定义moc关键字signals、slot和emit,因为这些名称将由第三方库使用,例如Boost。然后,要继续使用带有no_keywords标志的Qt信号和时隙,只需将源代码中Qt-moc所使用的关键字替换为相应的Qt宏Q_SIGNALS(或Q_SIGNAL)、Q_SLOTS(或Q-SLOT)和Q_EMIT。
在需要信号发送方信息的情况下,Qt提供了QObject::sender()函数,它返回一个指向发送信号的对象的指针。如下所示:

void MyWidget::on_pushButton_clicked()
{
    QPushButton *button = static_cast<QPushButton*>(QObject::sender());
    qDebug() << button->text(); 
}

以上内容参考:https://doc.qt.io/qt-5/signalsandslots.html
总结一下:
1.优点
①类型安全。需要关联的信号的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的参数类型和参数个数相同。若信号签名不一致,编译器会报错。
②松散耦合。信号机制减弱了Qt对象的耦合度。发送信号的Qt对象无需知道是那个对象的那个信号接收它发出的信号,它只需在适当的时间发送适当的信号即可。Qt可以保证了适当的得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。
③灵活性。一个信号可以关联多个,或多个信号关联同一个
2.缺点
速度较慢。与回调函数相比,信号机制运行速度比直接调用非虚函数慢10倍。
原因:
①需要定位接收信号的对象。
②安全地遍历所有关联
③编组、解组传递参数。
④多线程的时候,信号需要排队等待。
然而,与创建对象的new操作及删除对象的delete操作相比,信号的运行代价只是他们很少的一部分。信号机制导致的这点性能损耗,对实时应用程序是可以忽略的。

原文链接:Qt基础之四:Qt信号机制原理及优缺点_草上爬的博客-CSDN博客


http://www.niftyadmin.cn/n/5276.html

相关文章

对象的比较(上)PriorityQueue中的底层源码解析

作者&#xff1a;~小明学编程 文章专栏&#xff1a;Java数据结构 格言&#xff1a;目之所及皆为回忆&#xff0c;心之所想皆为过往 目录 问题引入 offer() 扩容 构造方法 grow() siftUp() siftUpComparable&#xff08;&#xff09; 问题引入 问题是这样的&#xff0c…

ThreadLocal InheritableThreadLocal TransmittableThreadLocal简单使用

需求: 主线程向异步线程传递用户token信息, 关于线程之间数据传递的几个关键对象, 主线程向子线程传递时,可以通过InheritableThreadLocal对象使用ExecutorService或者CompletableFuture执行时, 线程池内线程复用的原因,同时Thread对象内的inheritableThreadLocals值一是null…

Android App开发超实用实例 | 约束布局

从多个角度介绍约束布局设计中的控件定位。 01、约束布局基础 从 Android Studio 2.3版本起&#xff0c;约束布局是Android Studio布局文件的默认布局。其他布局方式在实现复杂一些的布局设计时存在多种或多个布局嵌套的情况&#xff0c;设备调用这样的布局文件就需要花费更多…

对于手机号和邮箱的格式验证

手机号和邮箱号的格式验证&#xff1a; package regex;import java.util.Scanner;public class Regex {public static void main(String[] args) { // Phone(); // Mail();}/** 1.* 手机号格式的验证*/public static void Phone(){while (true) {Scanner scanne…

(End)参与工作流研发的这8年

2022年6月6日&#xff0c;我们进入了java工作流平台的第3个年头&#xff0c;在这三年里&#xff0c;平台完成了上100万次的业务审批验证&#xff0c;经历了从能运行-->可使用--->基本能用--->全业务能力--->近期修正的运行性能危机应对&#xff0c;我们已完成了从工…

Spring Boot与Shiro实现权限管理04

1.实现用户管理 1.1 用户列表 首先创建dto&#xff0c;用于请求与响应数据的传输。在common包下创建dto包&#xff0c;在该包下创建UserDto.java类。 Data AllArgsConstructor NoArgsConstructor public class UserDto implements Serializable {private Integer id;private…

动态规划---不相交的线,最长公共子序列,最大子数组和

代码随想录day53 动态规划—不相交的线&#xff0c;最长公共子序列&#xff0c;最大子数组和 文章目录1.leetcode1143. 最长公共子序列1.1 思路及做题步骤1.2 代码示例2.leetcode1035. 不相交的线2.1 思路及做题步骤2.2 代码示例3.leetcode53. 最大子数组和3.1 思路及做题步骤3…

位运算常用技巧以及练习

几个有趣的操作 利用或操作|和空格将英文字符转换成小写 // 可以变成小写i : a | fmt.Printf("%c\n", i)j : A | fmt.Printf("%c\n", j)利用与操作&和下划线把英文字符转换成大写 // 可以变成大写m : b & _n : B & _fmt.Printf("%c\n…