Qt:信号槽

news2024/11/22 21:38:57

一. 信号槽概念

信号槽 是 Qt 框架中一种用于对象间通信的机制 。它通过让一个对象发出信号,另一个对象连接到这个信号的槽上来实现通信。信号槽机制是 Qt
的核心特性之一,提供了一种灵活且类型安全的方式来处理事件和数据传递。


1. 信号的本质

QT中,信号由三部分组成:

  1. 信号源 :哪个控件发出的信号
  2. 信类别 :什么类别的信号
  3. 信号处理方式 :通过信号处理函数,当信号被触发时执行。

2. 槽的本质

本身就是一个成员函数 ,负责对QT中产生的信号进行处理。

在编写代码时,槽函数的编写等同于类的成员函数。

举例

  • 有一个按钮,当点击这个按钮时,会关闭当前窗口。

  • connect(pushButton, &QPushButton::clicked, this, &MainWindow::close)
    对于该例来说:

    • 信号源 :按钮 pushButton
    • 信号类别 :点击信号 clicked
    • 信号处理方式 :利用连接connect 与槽函数slot(即close)进行处理

3.信号与槽的连接类型

  • Qt::AutoConnection
      默认值,使用这个值则连接类型会在信号发送时决定。
      如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。
      如果接收者和发送者不在同一个线程,则自动使用Qt::QueuedConnection类型。

  • Qt::DirectConnection
      槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成崩溃。

  • Qt::QueuedConnection
      槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。

  • Qt::BlockingQueuedConnection
      槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

  • Qt::UniqueConnection
      这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

4. 标准信号槽

即 Qt框架 中内置的信号槽:

Qt框架中,存在一些标准信号(Standard Signals)和标准槽(Standard
Slots)的已定义信号和槽函数。这些标准信号和槽提供了一些常见的功能,可以在不同的Qt对象之间进行连接。

  • 标准信号如:QPushButton、QLineEdit、QSlider等。
  • 标准槽如:对于QWidget,有show()、hide()、close()等。

5. 信号槽 实例

我们尝试写一个关闭窗口按钮

举例

我们将使用的标准信号 以及 槽函数:

// 单击按钮后发出的信号
[signal] void QAbstractButton::clicked(bool checked = false)
// 关闭窗口的槽函数
[slot] bool QWidget::close();

我们需要在mainWindow.ui中插入一个QPushButton类(不要忘记更改objectName)
在这里插入图片描述

随后通过 connect函数 将该功能实现出来:

// 单击按钮关闭窗口
connect(ui->closeBtn, &QPushButton::clicked, this, &MainWindow::close);

在上面的例子中:

  • 信号(Signal):&QPushButton::clicked。这里的信号是QPushButton类的clicked信号。当用户点击按钮时,QPushButton对象会发出(emit)一个clicked信号。

  • 槽(Slot):&MainWindow::close。这里的槽是MainWindow类的close成员函数。槽可以是任何普通的成员函数,它被设计为响应特定信号的函数。


二. 自定义信号槽

当QT提供的标准信号槽无法满足需求时,我们可以设计所需的信号与槽的功能,最后通过connect连接以实现功能。

当进行自定义信号槽时,需要遵循以下规则:

  • 当我们编写新的类时,要让其继承Qt的某些标准类

    • 继承自QObject:类必须直接或间接地继承自QObject,以便能够使用信号和槽机制
    • Q_OBJECT 宏:在类的声明中,使用Q_OBJECT宏进行标记。
    • 信号与槽函数声明:在类内部声明自定义信号和槽函数。信号声明只需要在signals部分声明,槽函数声明则需要在public slots或private slots部分声明

    // 在头文件派生类的时候,首先如下面的写法引入Q_OBJECT宏:
    class MyMainWindow : public QWidget
    {
    Q_OBJECT
    // … …
    }


1. 自定义槽函数

自定义槽函数有两种写法:

法一

一种是当我们使用代码创建控件时,自定义槽函数后手动连接以及实现。

在这里插入图片描述
随后我们在widget.cpp中手动进行创建按钮以及连接信号的操作。

在这里插入图片描述
结果显示:

在这里插入图片描述


法二

我们通过图形化界面创建控件,并利用Qt Creator的功能自动连接。

在这里插入图片描述
此时Qt Creator会自动生成槽函数的定义与声明:

在这里插入图片描述
且此时,我们只需要实现 on_pushButton_clicked() 函数,不需要进行connect就可以实现按钮的功能。

原因如下:

在这里插入图片描述


2. 自定义信号

以下是一些自定义信号时的要求注意事项 :

  1. 返回值必须是 void 类型(直接不写返回值就行)
  2. 信号需要使用 signals 关键字进行声明 , 使用方法类似于public等
  3. 信号函数只需要声明, 不需要定义 (没有函数体实现)
  4. 如要要发射自定义信号,一般在信号函数前加emit ,表示发射
    • 发送信号的本质就是调用信号函数
    • emit 关键字只是发出信号的标志,没有特殊含义,可以省略

举例

我们首先在头文件中定义信号
在这里插入图片描述
再用connect将自定义信号与槽连接起来、可以直接在构造函数中发送信号,也可以通过其他函数发送信号,我们这里通过按钮按下的槽函数发送信号。
在这里插入图片描述


3. 带参 信号槽

信号和槽都可以带有参数、自然也有函数重载,两者之间满足规则:

信号和槽的参数类型需一致,且信号的参数个数不能少于槽(可以多于)。

在这里插入图片描述


三. 信号槽的意义

  1. 解耦合
    在传统的编程模式中,一个对象A直接调用另一个对象B的函数,形成强耦合关系。如果对象B的函数接口发生变化,那么对象A也需要相应地修改。这种直接依赖关系使得代码难以维护和扩展。信号槽机制通过信号和槽的连接,实现了对象间的间接通信,降低了对象之间的耦合度。

  2. 易于扩展和维护
    由于信号和槽减少了对象间的直接依赖,当系统需要新增功能或者修改现有功能时,往往只需要添加或修改相关的信号和槽,而不需要对其他组件进行大幅度修改。这大大提高了代码的可维护性和可扩展性。

  3. 支持异步通信
    在传统的同步调用中,调用方必须等待被调用方处理完成后才能继续执行,这在某些情况下会导致程序的响应性不佳。信号槽机制支持异步通信,即发送信号后,发送方可以继续其它操作,而不需要等待接收方处理完毕。这对于提高程序的响应性和性能尤为重要。


四. 信号阻塞

| bool QObject::blockSignals(bool block)
—|—

  • 参数
    • block:一个布尔值,指示是否应阻塞对象的信号。如果blocktrue,则对象的信号将被阻塞;如果为false,则取消信号阻塞。
  • 返回值
    • 函数返回一个布尔值,指示在调用此函数之前是否已阻塞对象的信号。
功能描述
  1. 信号阻塞 :当block参数为true时,调用blockSignals(true)将阻止对象发出任何信号。这意味着即使对象的状态发生了改变(例如按钮被点击),与其关联的任何槽函数都不会被调用。

  2. 信号解除阻塞 :当block参数为false时,调用blockSignals(false)将取消对象的信号阻塞状态。此时,如果对象的状态发生改变,与其关联的槽函数将正常被调用。

  3. 作用范围 :在Qt中,信号和槽机制是作用于整个对象树的。这意味着如果你在一个父对象上调用blockSignals(true),那么该父对象及其所有子对象发出的信号都将被阻塞。这是因为在Qt中,子对象继承其父对象的信号阻塞状态。

  4. 返回值的意义blockSignals函数的返回值表示在调用该函数之前对象的信号阻塞状态。这可以用于保存并恢复对象的原始信号阻塞状态。

实际应用
  • 性能优化 :当需要频繁更改对象状态但不希望每次都触发信号和槽机制时,可以使用blockSignals(true)来暂时阻塞信号,以减少不必要的函数调用。完成状态更改后,可以使用blockSignals(false)来恢复信号的正常发出。
  • 避免不必要的副作用 :有时,更改一个对象的状态可能会触发一系列不希望发生的动作。通过调用blockSignals(true),可以确保这些动作不会发生,从而避免不必要的副作用。
注意事项
  • 调用blockSignals(true)后,请确保在适当的时候调用blockSignals(false)来恢复信号的正常发出,否则对象将永远不再发出其信号。
  • 在多线程环境中使用blockSignals时需要特别小心,以确保线程安全。

五. 信号槽断连

Qt中可以使用connect连接信号与槽、同时也可以使用disconnect 断开某个信号与槽的连接

disconnect()函数有几种重载形式,可以根据需要选择使用。

由于一般不用disconnect 断开连接,下面进行简单举例:

示例1:断开特定信号和槽
假设有一个按钮(QPushButton)和一个标签(QLabel),当按钮被点击时,标签的文本会改变。如果在某个时刻想要停止这种行为,可以使用disconnect()断开它们之间的连接:

QPushButton *button = new QPushButton("Click me");
QLabel *label = new QLabel("Hello");

// 连接信号和槽
QObject::connect(button, &QPushButton::clicked, [label]() {
    label->setText("Button Clicked!");
});

// 假设在某个条件下,我们需要断开上面建立的连接
QObject::disconnect(button, &QPushButton::clicked, nullptr, nullptr);

这里,disconnect()的调用断开了button的clicked信号与所有槽之间的连接。

示例2:断开所有与对象相关的连接
如果你想断开一个对象的所有信号与槽的连接,可以简单地传递该对象作为参数给disconnect():

// 断开与button相关的所有信号和槽的连接
QObject::disconnect(button);

这将断开button发出的所有信号与任何槽之间的连接,同时也断开任何信号到button槽的连接。

示例3:断开特定的信号和特定的槽
如果你只想断开一个特定信号与一个特定槽之间的连接,可以这样做:

// 假设有一个自定义槽函数
void customSlot();

// 连接信号和槽
QObject::connect(button, &QPushButton::clicked, this, &MyClass::customSlot);

// 在某个条件下,只断开这个特定的信号和槽的连接
QObject::disconnect(button, &QPushButton::clicked, this, &MyClass::customSlot);

这里,只有button的clicked信号与MyClass的customSlot槽之间的连接被断开。

示例4:使用返回值断开连接
connect()函数返回一个QMetaObject::Connection对象,可以用来在稍后断开连接:

QMetaObject::Connection conn = QObject::connect(button, &QPushButton::clicked, []() {
    qDebug() << "Button clicked!";
});

// 断开连接
QObject::disconnect(conn);

这种方式允许对特定的连接进行更精确的控制。

使用disconnect()可以灵活地管理信号和槽之间的连接状态,根据应用程序的需要动态调整其行为。


六. lamda表达式的使用

这里是关于lamda表达式的一篇文章:

【C++11】lambda表达式
的定义、性质和用法

在Qt
5及以上版本中,connect()函数支持使用lambda表达式作为槽函数。这使得在连接信号和槽时可以直接在参数中编写逻辑处理代码,而不需要定义额外的槽函数。这种方式可以让代码更加紧凑和灵活,特别是当槽函数只在一个地方使用且逻辑简单时。

1. 基本用法

下面是一个使用lambda表达式作为槽函数的基本示例:

QPushButton *button = new QPushButton("Click me");
QObject::connect(button, &QPushButton::clicked, []() {
    qDebug() << "Button was clicked!";
});

在该例中,当按钮被点击时,会执行lambda表达式内的代码,即打印一条消息到调试控制台。

2. 捕获局部变量

Lambda表达式可以捕获上下文中的变量,以便在表达式内部使用。例如,假设我们想在按钮点击时改变一个标签(QLabel)的文本:

QPushButton *button = new QPushButton("Change Label");
QLabel *label = new QLabel("Original Text");

QObject::connect(button, &QPushButton::clicked, [label]() {
    label->setText("Text after click");
});

这里,lambda表达式通过捕获label指针,可以在按钮被点击时修改标签的文本。

3. 使用捕获列表捕获变量

我们知道:Lambda表达式的捕获列表允许以不同的方式捕获变量,例如按值捕获(拷贝)或按引用捕获。使用=捕获所有局部变量的副本,使用&按引用捕获所有局部变量。

int count = 0;
QPushButton *button = new QPushButton("Increase Count");

QObject::connect(button, &QPushButton::clicked, [=]() mutable {
    qDebug() << "Count:" << ++count;
});
  • 注意:由于默认情况下lambda表达式是不允许修改捕获的变量的,如果想在lambda表达式内修改按值捕获的变量,需要在参数列表后添加mutable关键字。但是,这里的count变量实际上是按值捕获的副本,外部的count变量不会被修改。为了确保外部变量也被修改,应该按引用捕获:

    int count = 0;
    QPushButton *button = new QPushButton(“Increase Count”);

    QObject::connect(button, &QPushButton::clicked, & {
    qDebug() << “Count:” << ++count;
    });

  • 使用lambda表达式作为槽函数可以增加代码的灵活性和简洁性,尤其适用于处理简单的逻辑或者当我们不想为了一个简单的操作而专门去定义一个槽函数的情况。

参考

Qt之信号与槽的的五种连接类型 - CSDN

Qt 信号与槽详解 - CSDN

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

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

相关文章

SpringBoot与MongoDB深度整合及应用案例

SpringBoot与MongoDB深度整合及应用案例 在当今快速发展的软件开发领域&#xff0c;NoSQL数据库因其灵活性和可扩展性而变得越来越流行。MongoDB&#xff0c;作为一款领先的NoSQL数据库&#xff0c;以其文档导向的存储模型和强大的查询能力脱颖而出。本文将为您提供一个全方位…

大数据调度组件之Apache DolphinScheduler

Apache DolphinScheduler 是一个分布式易扩展的可视化 DAG 工作流任务调度系统。致力于解决数据处理流程中错综复杂的依赖关系&#xff0c;使调度系统在数据处理流程中开箱即用。 主要特性 易于部署&#xff0c;提供四种部署方式&#xff0c;包括Standalone、Cluster、Docker和…

ThinkPHP6门面(Facade)

门面 门面&#xff08;Facade&#xff09; 门面为容器中的&#xff08;动态&#xff09;类提供了一个静态调用接口&#xff0c;相比于传统的静态方法调用&#xff0c; 带来了更好的可测试性和扩展性&#xff0c;你可以为任何的非静态类库定义一个facade类。 系统已经为大部分…

MySQL win安装 和 pymysql使用示例

目录 一、MySQL安装 下载压缩包&#xff1a; 编写配置文件&#xff1a; 配置环境变量&#xff1a; 初始化服务和账户 关闭mysql开机自启&#xff08;可选&#xff09; 建议找一个数据库可视化软件 二、使用pymysql操作数据库 安装pymysql 示例代码 报错处理 一、My…

Parker派克防爆电机在实际应用中的安全性能如何保证?

Parker防爆电机确保在实际应用中的安全性能主要通过以下几个方面来保证&#xff1a; 1.防爆外壳设计&#xff1a;EX系列电机采用强大的防爆外壳&#xff0c;设计遵循严格的防爆标准&#xff0c;能够承受内部可能发生的爆炸而不破损&#xff0c;利用间隙切断原理&#xff0c;防…

空间与单细胞转录组学的整合定位肾损伤中上皮细胞与免疫细胞的相互作用

result 在空间转录组图谱中对人类肾脏进行无监督映射和细胞类型鉴定 我们试图在H&E染色的人类参考肾切除标本组织切片上直接映射转录组特征。该组织来自一名59岁的女性&#xff0c;其肾小球闭塞和间质纤维化程度最低&#xff08;分别影响不到10%的肾小球或肾实质&#xff…

greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用

简略图 greater<>()(a, b) a > b 返回true&#xff0c;反之返回false less<>()(a, b) a < b 返回true&#xff0c;反之返回false 在cmp中使用&#xff08;正着理解&#xff09; 规则返回true时a在前&#xff0c;反之b在前 在priority_queue中使用 &#xff…

详细描述一下Elasticsearch索引文档的过程?

大家好&#xff0c;我是锋哥。今天分享关于【详细描述一下Elasticsearch索引文档的过程&#xff1f;】面试题。希望对大家有帮助&#xff1b; 详细描述一下Elasticsearch索引文档的过程&#xff1f; Elasticsearch的索引文档过程是其核心功能之一&#xff0c;涉及将数据存储到…

入门车载以太网(6) -- XCP on Ethernet

目录 1.寻址方式 2.数据帧格式 3.特殊指令 4.使用实例 了解了SOME/IP之后&#xff0c;继续来看看车载以太网在汽车标定领域的应用。 在汽车标定领域XCP是非常重要的协议&#xff0c;咱们先来回顾下基础概念。 XCP全称Universal Measurement and Calibration Protocol&a…

Python中常用的函数介绍

Python中常用的几种函数 1、input函数 input()函数&#xff1a;主要作用是让用户输入某个内容并接收它。 #输入你的年龄 >>> age input("my age is :") my age is :20 执行代码后输入年龄&#xff0c;年龄被存放到age变量中&#xff0c;执行print后终端会…

Ubuntu从入门到精通(二)远程和镜像源配置齐全

Ubuntu从入门到精通(二) 1 常见操作配置 1.1 英文语言配置 1.1.1 打开设置 1.1.2 设置语言为英文 1.1.3 重启生效 1.1.4 再次进入,选择更新名字 1.1.5 再次进入,发现已经变成了英文 1.2 输入法配置 1.3 rustdesk安装 1.3.1 Windows系统配置 登陆:https://github.com…

卷积神经网络(CNN)中的池化层(Pooling Layer)

池化层&#xff08;Pooling Layer&#xff09;&#xff0c;也被称为下采样层&#xff0c;是深度学习神经网络中常用的一种层级结构。它通常紧跟在卷积层之后&#xff0c;对卷积层输出的特征图进行下采样操作。 一、定义与功能 池化层的主要作用是通过减少特征图的尺寸来降低计算…

【linux硬件操作系统】计算机硬件常见硬件故障处理

这里写目录标题 一、故障排错的基本原则二、硬件维护注意事项三、关于最小化和还原出厂配置四、常见故障处理及调试五、硬盘相关故障六、硬盘相关故障&#xff1a;硬盘检测问题七、硬盘相关故障&#xff1a;自检硬盘报错八、硬盘相关故障&#xff1a;硬盘亮红灯九、硬盘相关故障…

《操作系统》实验内容 实验二 编程实现进程(线程)同步和互斥(Python 与 PyQt5 实现)

实验内容 实验二 编程实现进程&#xff08;线程&#xff09;同步和互斥 1&#xff0e;实验的目的 &#xff08;1&#xff09;通过编写程序实现进程同步和互斥&#xff0c;使学生掌握有关进程&#xff08;线程&#xff09;同步与互斥的原理&#xff0c;以及解决进程&#xf…

智慧路面管理系统平台 智慧照明 智慧市政 智慧交通

智慧路面管理系统平台   智慧路面管理系统平台&#xff0c;旨在提高城市道路的智能化水平和交通效率。该系统通过集成传感器、摄像头、监控设备、大数据、云计算等多种技术手段&#xff0c;实现对道路状况和交通流量的实时监测与分析&#xff0c;从而提供精准的交通数据和智能…

数据结构 ——— 判断一棵树是否是完全二叉树

目录 满二叉树和完全二叉树示意图 手搓一个完全二叉树 代码实现 满二叉树和完全二叉树示意图 注意区分满二叉树和完全二叉树 满二叉树的每一层都是满的&#xff0c;也就是除了叶子节点&#xff0c;其他节点都有左右节点 完全二叉树的最后一层不一定是满的&#xff0c;但是从…

Vue_Router权限控制:不同角色显示不同路由

写在前面 在Vue中&#xff0c;Router是一个官方提供的用于处理应用程序路由的插件。它允许我们创建单页应用程序&#xff08;SPA&#xff09;&#xff0c;其中不同的页面和组件可以通过URL进行导航和展示。使我们可以轻松地创SPA&#xff0c;并实现可复用和可组合的组件…

java多线程并发执行方法或者调用接口

在开发过程中有时需要检查某个接口或者某个方法是否存在并发安全问题&#xff0c;我们会用到jmeter 、AB 等压测工具辅助我们完成代码测试&#xff0c;虽然这些工具功能很强大&#xff0c;也很好用&#xff0c;但是在开发过程中来使用还是不如直接执行Test 或者main 方法来的方…

Python小游戏28——水果忍者

首先&#xff0c;你需要安装Pygame库。如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 《水果忍者》游戏代码&#xff1a; 【python】 import pygame import random import sys # 初始化Pygame pygame.init() # 设置屏幕尺寸 …

测评部署和管理 WordPress 最方便的面板

新版宝塔面板快速搭建WordPress新手教程 - 倚栏听风-Morii - 博客园 初学者使用1Panel面板快速搭建WordPress网站 - 倚栏听风-Morii - 博客园 可以看到&#xff0c;无论是宝塔还是1Panel&#xff0c;部署和管理WordPress都有些繁琐&#xff0c;而且还需要额外去配置Nginx和M…