Qt开发:信号与槽的介绍和使用

文章目录

    • 一、信号与槽的概念
    • 二、信号与槽的优点
    • 三、信号与槽的连接方式
      • 1.connect()函数的不同参数形式
      • 2.使用sender()获得信号发射者
      • 3.信号与槽机的连接方式
    • 四、信号与槽的实现原理

一、信号与槽的概念

信号(Signal)就是在特定情况下被发射的事件,例如PushButton最常见的信号就是鼠标单击时发送的clicked()信号。GUI程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。
槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的C++函数的声明定义是一样的,可以定义在类的任何部分(public、private或protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,管理的槽函数被自动执行。
信号与槽关联是用QObject::connect()函数实现的,其基本格式是:

QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

connect()是QObject类的一个静态函数,而QObject是所有Qt类的基类,在实际调用时可以忽略前面的限定符,所以可以直接写为:

QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

其中sender是发射信号的对象的名称,signal()是信号名称,信号可以看作是特殊的函数,需要带括号,有参数时还需要指明参数,receiver是接收信号的对象名称,slot()是槽函数的名称,需要带括号,有参数时还需要指明参数。
关于信号与槽的使用,有以下一些规则需要注意:

  1. 一个信号可以连接多个槽函数,当一个信号与多个槽函数关联时,槽函数按照建立连接时的顺序依次执行。当信号和槽函数带有参数时,在connect()函数里,要写明参数的类型,但可以不写参数名称。
  2. 多个信号可以连接同一个槽函数
  3. 一个信号可以连接另一个信号;当一个信号发射时,也会发射另一个信号,从而是新特殊的功能。
  4. 信号与槽的参数个数和类型需要一致,至少信号的参数不能少于槽的参数,如果不匹配,会出现编译错误或运行错误。
  5. 在使用信号和槽的类中,必须在类的定义中加入宏Q_OBJECT。
  6. 当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。

二、信号与槽的优点

在Qt中,信号与槽(Signals and Slots)是一种用于实现对象间通信的重要机制。信号与槽机制的本质是一种事件处理和消息传递系统,它允许对象在特定事件发生时通信并响应这些事件,而不需要对象直接调用彼此的方法。这种机制具有以下关键特征和本质:

  1. 解耦性(Decoupling):信号与槽机制将发射信号的对象与接收信号的槽函数的对象解耦。这意味着对象之间不需要直接知道彼此的存在或具体的实现细节,从而使代码更加灵活和可维护。
  2. 事件驱动(Event-Driven):信号与槽机制是一种事件驱动的编程范式。当一个对象发射信号时,其他对象可以注册槽函数来监听和响应这些信号,实现了异步和事件驱动的编程模型。
  3. 多对多连接:一个信号可以连接到多个槽,一个槽也可以连接到多个信号。这种多对多的连接允许广泛的对象之间进行通信,无需复杂的管理。
  4. 支持跨线程通信:Qt的信号与槽机制支持线程间通信,使得多线程应用程序中的异步消息传递更容易。
  5. 类型安全:信号与槽机制在编译时进行类型检查,以确保信号与槽之间的连接是类型安全的,从而减少运行时错误。
  6. 可扩展性:开发人员可以自定义信号和槽,从而为自定义对象添加新的事件和行为,使得Qt应用程序可以轻松地扩展和定制。

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/f8abd4c7490247f2a38768b47788c0db.png#pic_center

三、信号与槽的连接方式

信号与槽是Qt的一个核心特点,也是它区别于其他框架的重要特性。信号与槽是对象进行通信的机制,也需要由Qt的元对象系统支持才能实现的。Qt使用信号与槽的机制实现对象间通信,它隐藏了复杂的底层实现,完成信号与槽的关联后,发射信号时不需要知道Qt是如何找到槽函数的,Qt的信号与槽机制与C++ Builder的”事件—响应“比较类似,但是更加灵活。
某些开发架构使用回调函数(callback)实现对象间通信。与回调函数相比,信号与槽的执行速度稍微慢一点,因为需要查找连接的对象和槽函数,但是这种差别在应用程序运行时是感觉不到的,而其提供的灵活性却比回调函数强很多。

1.connect()函数的不同参数形式

QObject::connect()函数由多重参数形式,一种参数形式的函数原型是:

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

使用这种参数形式的connect()进行信号与槽函数的连接时,传递的是函数签名的字符串形式。一般语法如下所示:

connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

这里使用了宏SIGNAL()和SLOT()指定信号和槽函数,而且如果信号和槽函数带有参数,还需要注明参数类型。如:

connect(sender, SIGNAL(signal(int)), receiver, SLOT(slot(int)));

缺点:

字符串方式缺乏类型检查,容易出现拼写错误而导致连接失败。
编译时无法验证信号和槽的参数是否匹配,错误只能在运行时发现。

另一种参数形式的connect()函数的原型是:

QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)

优点:

函数指针语法更清晰,便于代码维护。
编译时进行类型检查,减少运行时错误。

对于具有默认参数的信号与槽(即信号和名称是唯一的,没有参数不同而同名的两个信号),可以使用这种函数指针形式进行关联。如:

connect(lineEdit, &QLineEdit::textChanged, this, &widget::on_textChanged);

但是对于具有不同参数的同名信号就不能采用函数指针的方式进行信号与槽的关联,例如QSpinBox有两个valueChanged()信号,分别是:

void QSpinBox::valueChanged(int i);
void QSpinBox::valueChanged(const QString &text);

如果还使用上述的方式进行关联,编译就会报错。在Qt5.7版本以上,解决信号与槽函数重载的问题可以通过使用QOverload类,它是Qt提供的一个工具类,用于指定特定的重载函数,通过使用QOverload类,可以确保连接到正确的重载函数。

// 连接到 int 参数版本的 on_valueChanged
connect(sender, QOverload<int>::of(&QSpinBox::valueChanged), receiver, QOverload<int>::of(&widget::on_valueChanged));
// 连接到 QString 参数版本的 on_valueChanged
connect(sender, QOverload<const QString &>::of(&valueChanged::valueChanged), receiver, QOverload<const QString &>::of(&widget::on_valueChanged));

如果使用的 Qt 版本低于 5.7,可以用 static_cast 明确指定信号的类型。我们有如下的信号和槽函数:
信号:

class SenderClass : public QObject {
    Q_OBJECT
signals:
    void mySignal(int value);
    void mySignal(const QString &text);
};

槽:

class ReceiverClass : public QObject {
    Q_OBJECT
public slots:
    void mySlot(int value);
    void mySlot(const QString &text);
};
SenderClass *sender = new SenderClass;
ReceiverClass *receiver = new ReceiverClass;

// 连接 int 参数的信号和槽
connect(sender, static_cast<void (SenderClass::*)(int)>(&SenderClass::mySignal),
        receiver, static_cast<void (ReceiverClass::*)(int)>(&ReceiverClass::mySlot));

// 连接 QString 参数的信号和槽
connect(sender, static_cast<void (SenderClass::*)(const QString &)>(&SenderClass::mySignal),
        receiver, static_cast<void (ReceiverClass::*)(const QString &)>(&ReceiverClass::mySlot));

如果连接过程需要中间层处理,或者使用的代码环境中槽函数比较复杂,不想使用QOverload类或者static_cast 进行连接,可以使用 Lambda 表达式。

SenderClass *sender = new SenderClass;
ReceiverClass *receiver = new ReceiverClass;

// 连接 int 参数的信号
connect(sender, static_cast<void (SenderClass::*)(int)>(&SenderClass::mySignal), this, [receiver](int value) {
    receiver->mySlot(value);
});

// 连接 QString 参数的信号
connect(sender, static_cast<void (SenderClass::*)(const QString &)>(&SenderClass::mySignal), this, [receiver](const QString &text) {
    receiver->mySlot(text);
});

2.使用sender()获得信号发射者

在槽函数里,使用QObject::sender()可以获取信号发射者的指针,如果知道信号发射者的类型,可以将指针投射为确定的类型,然后使用这个确定类的接口函数。
例如在QSpinBox的valueChanged(int)信号的槽函数里,可以通过sender()和qobject_cast获得信号发射者的指针,从而对信号发射者进行操作。

QSpinBox *spinBox = qobject_cast<QSpinBox*>(sender());

3.信号与槽机的连接方式

Qt中的信号与槽机制通过不同方式的连接实现了对象之间的通信。以下是几种连接方式:

  1. 自动连接(Auto Connection):这是最常见的连接方式,通常在Qt Designer中进行。当你使用Qt Designer将信号与槽连接时,Qt会自动选择适当的连接类型。例如,当你在UI设计器中将按钮的clicked()信号与槽函数连接时,Qt通常会自动选择Qt::AutoConnection,这意味着它会在事件循环中自动将信号传递给槽。
  2. 直接连接(Direct Connection):使用Qt::DirectConnection可以创建直接连接。在这种连接中,信号会立即调用槽函数,不经过事件队列。这通常用于在同一线程内的对象之间建立连接。
  3. 队列连接(Queued Connection):使用Qt::QueuedConnection可以创建队列连接。在这种连接中,信号会被发送到接收者的事件队列,稍后由事件循环处理。这对于在不同线程中的对象之间建立连接很有用。
  4. 阻塞队列连接(BlockingQueuedConnection):使用 Qt::BlockingQueuedConnection 可以创建阻塞队列连接。与 Qt::QueuedConnection 类似,信号会被发送到接收者的事件队列中,但不同的是,在Qt::BlockingQueuedConnection中,发射信号的线程会等待槽函数执行完成后再继续执行。这对于需要等待槽函数完成后才能继续执行的线程间通信非常有用。

四、信号与槽的实现原理

在 Qt 中,信号是通过元对象系统来与指定的槽函数进行关联和调用的。元对象系统使得 Qt 能够在运行时动态地查找和连接信号与槽,确保信号触发时正确地找到槽函数并执行它。
信号和槽的实现原理基于 C++ 的元对象系统,具体包括以下几个关键部分:

  1. 代码编译阶段:在编译时,Qt 使用元对象编译器(moc,Meta-Object Compiler)处理包含信号和槽声明的源代码文件。moc 生成了元对象代码,其中包含了类的元对象信息,包括信号和槽的描述。
  2. 元对象信息:每一个继承自 QObject 的类都有一个与之相关的元对象。元对象包含了该类的属性、方法和信号槽的描述。对于信号,元对象记录了它们的名称、参数类型以及它们的标识符。
  3. QObject 类的实例:在运行时,每个 QObject 类的实例都包含一个指向元对象的指针,这个元对象描述了该实例的类信息和信号槽。
  4. 信号发射:当一个对象发射一个信号(使用 emit 关键字),Qt 通过元对象系统来查找与该信号相关联的槽函数。元对象系统根据信号的名称和参数类型在元对象中查找匹配的槽函数。
  5. 信号和槽的连接:在使用 QObject::connect 函数将信号与槽连接时,Qt 会将信号的描述与槽函数的描述进行匹配。这通常是在应用程序的初始化阶段完成的。
  6. 信号触发:当信号被触发时,元对象系统会遍历与该信号相关联的槽函数,并按照它们的连接顺序调用这些槽函数。
  7. 参数传递:如果信号有参数,元对象系统会确保传递正确的参数给槽函数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自媒体图文视频自动生成软件|03| 页面和结构介绍

代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; 视频图片生成器 一个基于 Python 和 Web 的工具&#xff0c;用于生成带有文字和语音的视频以及图片。支持多种尺寸、…

(11)(2.2) BLHeli32 and BLHeli_S ESCs(二)

文章目录 前言 1 传递支持 前言 BLHeli 固件和配置应用程序的开发是为了允许配置 ESC 并提供额外功能。带有此固件的 ESC 允许配置定时、电机方向、LED、电机驱动频率等。在尝试使用 BLHeli 之前&#xff0c;请按照 DShot 设置说明进行操作(DShot setup instructions)。 1 传…

逻辑处理器核心指纹修改

navigator.hardwareConcurrency的属性,可以用来获取CPU的逻辑处理器核心数。 1、navigator.hardwareConcurrency接口定义&#xff1a; third_party\blink\renderer\core\frame\navigator_concurrent_hardware.idl // https://html.spec.whatwg.org/C/#navigator.hardwarecon…

Linux下的火墙管理及优化

从功能角度来讲 防火墙是位于内部网和外部网之间的屏障&#xff0c;它按照系统管理员预先定义好的规则来控制数据包的进 从功能实现角度来讲 火墙是系统内核上的一个模块netfilter(数据包过滤机制) 通过netfiler来管理kernel space中的策略 netfilter简介 Netfilter是Lin…

chrome允许http网站打开摄像头和麦克风

第一步 chrome://flags/#unsafely-treat-insecure-origin-as-secure 第二步 填入网址&#xff0c;点击启用 第三步 重启 Chrome&#xff1a;设置完成后&#xff0c;点击页面底部的 “Relaunch” 按钮&#xff0c;重新启动 Chrome 浏览器&#xff0c;使更改生效。

【Vue】Ego商城项目跟做

技术栈 Vue全家桶&#xff1a;Vue VueRouter Vuex Axios ElementUI 依赖安装 网络请求&#xff1a;npm install --save axios --no-fund Element&#xff1a;vue add element 后端相关依赖&#xff1a;npm install --save express cors mysql --no-fund token&#xff1a;np…

ALSA(4) --- CPU DAI实践

CPU_DAI实践 物理拓扑图 上图可知&#xff0c;从dma过来数据&#xff0c;会保存在DAI的一个FIFO队列中&#xff0c;数据是并行过来的各个通道数据&#xff0c;经过shift移位寄存器&#xff0c;再经过P2S并行转串行&#xff0c;再经过DAVC音量控制输出到GPIO端口 音频数据接口…

【开篇】.NET开源 ORM 框架 SqlSugar 系列

01. 前言 ☘️ 1.1 什么是ORM? 对象-关系映射&#xff08;Object-Relational Mapping&#xff0c;简称ORM&#xff09;&#xff0c;面向对象的开发方法是当今企业级应用开发环境中的主流开发方法&#xff0c;关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对…

EtherCAT Coe对象创建与通信

目录 前言使用SSC工具生成XML填充读写函数测试 前言 EtherCAT协议栈生成参考https://blog.csdn.net/qq_42039294/article/details/144061669 本文默认大家有EtherCAT基础的移植经验 使用SSC工具生成XML 首先确保COE是开启的 打开表格&#xff0c;编辑内容如下 更多的数据类…

Axure农业农村数据可视化大屏模板分享

在当今信息技术飞速发展的时代&#xff0c;数据可视化已成为各行各业提升管理效率、优化决策过程的重要手段。Axure作为一款强大的原型设计工具&#xff0c;凭借其高度的自定义能力和丰富的交互设计功能&#xff0c;在农业农村数据可视化领域展现出强大的潜力。本文将详细介绍A…

conda、pip同时安装包引起混乱问题剖析

一句话总结&#xff1a; 安装版本不一致时会有两个.dist-info文件夹&#xff08;举例&#xff1a;scapy-2.6.1.dist-info和scapy-2.4.3.dist-info&#xff09;&#xff0c;conda list和pip list依靠这两个文件夹进行包的识别&#xff08;疑似pip list识别老版本&#xff0c;co…

vue实现滚动条滑动到底部分页调取后端接口加载数据

一、案例效果 二、前提条件 接口返回数据 三、案例代码 子组件 const $emit defineEmits([cloneItem, updateList]);const props defineProps({rightList: {type: Array,},chartTableData: {type: Array as () > ChartListType[],},deleteChartInfo: {type: Object,}…

redis 底层数据结构

概述 Redis 6 和 Redis 7 之间对比&#xff1a; Redis6 和 Redis7 最大的区别就在于 Redis7 已经用 listpack 替代了 ziplist. 以下是基于 Redis 7基础分析。 RedisObject Redis是⼀个<k,v>型的数据库&#xff0c;其中key通常都是string类型的字符串对象&#xff0c;⽽…

arm rk3588 onnx转rknn

一、环境部署&#xff1a; https://github.com/airockchip/rknn_model_zoo/tree/main/examples/yolo11 从该网址下载yolo11的模型。支持80种类型检测 二、下载模型 进入examples/yolo11/model文件夹&#xff0c;执行 ./download_model.sh 如图&#xff1a; 三、模型转换…

Flutter 3.24.5安装配置——2024年11月26日

目录 1️⃣前置安装使用环境配置步骤安装Flutter SDK安装Android SDK修改文件默认安装位置&#xff08;.gradle, AVD&#xff09;开始项目 2️⃣执行结果&#x1fab2;Bug找不到**.jar文件 &#x1f517;参考链接 1️⃣前置安装 使用环境 Windows 11IDEA 2024.2.3Flutter 3.2…

Pytest-Bdd-Playwright 系列教程(13):钩子(hooks)

Pytest-Bdd-Playwright 系列教程&#xff08;13&#xff09;&#xff1a;钩子&#xff08;hooks&#xff09; 前言一、什么是钩子&#xff1f;二、Pytest-Bdd 提供的钩子一览三、钩子用法详解1. pytest_bdd_before_scenario2. pytest_bdd_after_scenario3. pytest_bdd_before_s…

23种设计模式-生成器(Builder)设计模式

文章目录 一.什么是生成器设计模式&#xff1f;二.生成器模式的特点三.生成器模式的结构四.生成器模式的优缺点五.生成器模式的 C 实现六.生成器模式的 Java 实现七.代码解析八. 总结 类图&#xff1a; 生成器设计模式类图 一.什么是生成器设计模式&#xff1f; 生成器模式&am…

HCIP——堆叠技术实验配置

目录 一、堆叠的理论知识 二、堆叠技术实验配置 三、总结 一、堆叠的理论知识 1.1堆叠概述&#xff1a; 是指将两台交换机通过堆叠线缆连接在一起&#xff0c;从逻辑上变成一台交换设备&#xff0c;作为一个整体参与数据的转发。 1.2堆叠的基本概念 堆叠系统中所有的单台…

Python - 函数(四)

函数&#xff1a;在编写程序的过程中&#xff0c;有某一功能代码块出现多次&#xff0c; 但是为了提高编写的效率以及代码的重用&#xff0c;所以把具有独立功能的代码块组织为一个小模块&#xff0c;这就是函数 ‌Python中的函数‌是一组被命名的可执行代码&#xff0c;用于完…

豆包MarsCode算法题:三数之和问题

问题描述 思路分析 1. 排序数组 目的: 将数组 arr 按升序排序&#xff0c;这样可以方便地使用双指针找到满足条件的三元组&#xff0c;同时避免重复的三元组被重复计算。优势: 数组有序后&#xff0c;处理两个数和 target - arr[i] 的问题可以通过双指针快速找到所有可能的组…