Qt6入门教程 8:信号和槽机制(连接方式)

目录

一.一个信号与槽连接的例子

二.第五个参数

1.Qt::AutoConnection

2.Qt::DirectConnection

3.Qt::QueuedConnection

4.Qt::BlockingQueuedConnection

5.Qt::UniqueConnection

三.信号

四.connect函数原型

五.信号与槽的多种用法

六.槽的属性


一.一个信号与槽连接的例子

#include <QObject>
#include <QDebug>

class Counter : public QObject
{
    Q_OBJECT

public:
    Counter() { m_value = 0; }

    int value() const
    {
        return m_value;
    }

public slots:
    void setValue(int value)
    {
        if (value != m_value)
        {
            m_value = value;
            emit valueChanged(value);
        }
    }

signals:
    void valueChanged(int newValue);

private:
    int m_value;
};

int main(int argc, char *argv[])
{
    Counter a, b;
    QObject::connect(&a, &Counter::valueChanged,
                     &b, &Counter::setValue);

    a.setValue(12); // a.value() == 12, b.value() == 12
    qDebug() << "a: " << a.value() << " b: " << b.value();
    b.setValue(48); // a.value() == 12, b.value() == 48
    qDebug() << "a: " << a.value() << " b: " << b.value();


    return 0;
}

#include "main.moc"

在调用a.setValue(12)时会发送valueChanged(12)信号,此时槽函数b.setValue(12)会被调用。然后b也会发送valueChanged(12)信号,但是由于没有槽连接到这个信号,所以这个信号被忽略了,不做处理。
需要注意的是,在setValue()函数中在赋值和发送信号之前做了 value != m_value的判断。这样做是为了防止特定情况下触发的无限循环调用,比如此时b.valueChanged()连接到了a.setValue()。

二.第五个参数

在上面的例子中,调用connect()函数时并没有指定第五个参数,因第五个参数一般不填,为默认值。

1.Qt::AutoConnection

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

2.Qt::DirectConnection

槽函数会在信号发送的时候直接被调用,槽函数和信号发送者在同一线程。效果看上去就像是直接在信号发送位置调用了槽函数,同步执行。
emit语句后面的代码将在与信号关联的所有槽函数执行完毕后才被执行。
无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。但需要注意的是,当信号和槽在不同的线程时,Qt::DirectConnection连接方式是不安全的,就像不能直接调用不同线程中的函数一样。但QObject::connect() 方法本身是线程安全的。

3.Qt::QueuedConnection

信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。
emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。
槽函数在接收者所依附线程执行。

4.Qt::BlockingQueuedConnection

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

// Qt部分源码如下
.....//其他代码
else if(type == Qt::BlockingQueuedConnection)
{
    if(currentThread == objectThread) // 如果是同一条线程,就死锁了
        qWarning("QMetaObject::invoke: Dead lock detected");
 
    QSemaphore semaphore; // 信号量
    QCoreApplication::postEvent(Object, new QMetaCallEvent(slot, 0, -1, 0, 0, qrgv, &semaphore));  // 将函数指针、函数参数、信号量的指针发送到事件队列
    semaphore.acquire(); // 默认形参为1,;获取1个数据,如果没有准备1个好数据,则阻塞
}
else
{
.... //其他代码
}

5.Qt::UniqueConnection

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

三.信号

声明信号使用signals关键字,发送信号使用emit关键字。
注意点:
1.所有的信号声明都是公有的,所以Qt规定不能在signals前面加public、private、protected。
2.所有的信号都没有返回值,所以返回值都用void。
3.所有的信号都不需要定义。
4.必须直接或间接继承自QOBject类,并且开头私有声明包含Q_OBJECT。
5.在同一个线程中,当一个信号被emit发出时,会立即执行其槽函数,等槽函数执行完毕后,才会执行emit后面的代码,如果一个信号链接了多个槽,那么会等所有的槽函数执行完毕后才执行后面的代码,槽函数的执行顺序是按照它们链接时的顺序执行的。不同线程中(即跨线程时),槽函数的执行顺序是随机的。
6.在链接信号和槽时,可以设置链接方式为:在发出信号后,不需要等待槽函数执行完,而是直接执行后面的代码,是通过connect的第5个参数。
7.信号与槽机制要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,信号的参数可以比槽函数的参数多,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。

四.connect函数原型

1.如:connect(pushButton, SIGNAL(clicked()), dialog, SLOT(close()));Qt4和Qt5都可以使用这种连接方式。

static QMetaObject::Connection connect(
    const QObject *sender,    //信号发送对象指针
    const char *signal,       //信号函数字符串,使用SIGNAL()
    const QObject *receiver,  //槽函数对象指针
    const char *member,       //槽函数字符串,使用SLOT()
    Qt::ConnectionType = Qt::AutoConnection);

2.如:connect(pushButton, &QPushButton::clicked, dialog, &QDialog::close);这是Qt5新增的连接方式,在编译期间就可以进行类型检查,推荐使用这种连接方式。

static QMetaObject::Connection connect(
    const QObject *sender,      //信号发送对象指针
    const QMetaMethod &signal,  //信号函数地址
    const QObject *receiver,    //槽函数对象指针
    const QMetaMethod &method,  //槽函数地址
    Qt::ConnectionType type = Qt::AutoConnection);

3.两者的对比

概述基于字符串的语法基于函子的语法
做类型检查的阶段运行时编译时
是支持行类型的隐式转换
是否支持信号连接到lambda表达式
是否支持槽的参数比信号的参数多(此时槽使用默认参数)
是否支持连接C++函数到QML函数

注:qt5之后,这种新型的写法,支持所有的函数类型,无需定义slots关键字也可以。

如何选择重载的信号和槽呢?例如,QLCDNumber有三个版本的display()槽:
●QLCDNumber::display(int)
●QLCDNumber::display(double)
●QLCDNumber::display(QString)
将槽连接到int版本的QSlider::valueChanged()信号, 两种语法的写法如下:

auto slider = new QSlider(this);
auto lcd = new QLCDNumber(this);

// 基于字符串的语法
connect(slider, SIGNAL(valueChanged(int)),
        lcd, SLOT(display(int)));

// 基于函子的语法,第一种方式(推荐)
connect(slider, &QSlider::valueChanged,
        lcd, static_cast<void (QLCDNumber::*)(int)>(&QLCDNumber::display));

// 基于函子的语法,第二种方式
void (QLCDNumber::*mySlot)(int) = &QLCDNumber::display;
connect(slider, &QSlider::valueChanged,
        lcd, mySlot);

// 基于函子的语法,第三种方式
connect(slider, &QSlider::valueChanged,
        lcd, QOverload<int>::of(&QLCDNumber::display));

// 基于函子的语法,第四种方式(C++14)
connect(slider, &QSlider::valueChanged,
        lcd, qOverload<int>(&QLCDNumber::display));

在需要信号发送方信息的情况下,Qt提供了QObject::sender()函数,它返回一个指向发送信号的对象的指针。如下所示:

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

五.信号与槽的多种用法

1.一个信号可以和多个槽相连
2.多个信号可以连接到一个槽
3.一个信号可以连接到另外的一个信号
4.槽可以被取消链接.
其写法和connect一样,只需要将connect换成disconnect即可。
disconnect() 通常以三种方式使用,如下面的示例所示:
1.断开myObject对象的信号与其他对象间的连接,使用后myObject发出的信号没有对应的槽函数进行响应

disconnect(myObject, nullptr, nullptr, nullptr);
//or
myObject->disconnect();

2.断开myObject对象的mySignal()信号与其他对象间的连接,使用后myObject发出的mySignal()信号没有对应的槽函数进行响应

disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr);
//or
myObject->disconnect(SIGNAL(mySignal()));

3.断开myObject对象的与myReceiver对象间的连接,使用后myObject发出mySignal()信号myReceiver没有对应的槽函数进行响应

disconnect(myObject, nullptr, myReceiver, nullptr);
//or
myObject->disconnect(myReceiver);

六.槽的属性

public slots:在这个区内声明的槽意味着所有对象都可将信号和之相连接。这对于组件编程非常有用,你能创建彼此互不了解的对象,将他们的信号和槽进行连接以便信息能够正确的传递。 
protected slots:在这个区内声明的槽意味着当前类及其子类能将信号和之相连接。
private slots:在这个区内声明的槽意味着只有类自己能将信号和之相连接。
注:信号和槽不能携带模板类参数

原文链接:https://blog.csdn.net/caoshangpa/article/details/135639126

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

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

相关文章

MetaGPT入门(一)

本文在Win11操作系统下进行&#xff0c;工具pycharm 一、环境准备 1.建议使用conda虚拟环境 安装anaconda参考&#xff1a;Windows10下Anaconda的安装_windows anaconda 路径-CSDN博客 打开Anaconda Powershell Prompt命令窗口&#xff0c;输入下面命令&#xff0c;创建3.1…

MyBatisPlus学习笔记三-核心功能

接上篇&#xff1a; MyBatisPlus学习笔记二-CSDN博客 1、核心功能-IService开发基础业务接口 1.1、介绍 1.2、引用依赖 1.3、配置文件 1.4、用例-新增 1.5、用例-删除 1.6、用例-根据id查询 1.7、用例-根据ids查询 2、核心功能-IService开发复杂业务接口 2.1、实例-更新 3、…

Spring高手之路-Spring在业务中常见的使用方式

目录 通过IOC实现策略模式 通过AOP实现拦截增强 1.参数检验 2.缓存逻辑 3.日志记录 通过Event异步解耦 通过Spring管理事务 1.声明式事务 2.编程式事务 3.需要注意的问题 不能在事务中处理分布式缓存 不能在事务中执行 RPC 操作 不过度使用声明式事务 通过IOC实现…

软件工程应用题汇总

绘制数据流图(L0/L1/L2) DFD/L0&#xff08;基本系统模型&#xff09; 只包含源点终点和一个处理(XXX系统) DFD/L1&#xff08;功能级数据流图&#xff09;在L0基础上进一步划分处理(XXX系统) 个人理解 DFD/L2&#xff08;在L1基础上进一步分解后的数据流图&#xff09; 数据…

蓝桥杯备赛 day 3 —— 高精度(C/C++,零基础,配图)

目录 &#x1f308;前言&#xff1a; &#x1f4c1; 高精度的概念 &#x1f4c1; 高精度加法和其模板 &#x1f4c1; 高精度减法和其模板 &#x1f4c1; 高精度乘法和其模板 &#x1f4c1; 高精度除法和其模板 &#x1f4c1; 总结 &#x1f308;前言&#xff1a; 这篇文…

C#中对浮点数NaN,PositiveInfinity,NegativeInfinity的特殊处理

NAN NAN 整体意思为Not a Number 不是一个数&#xff0c; NaN&#xff08;Not a Number&#xff0c;非数&#xff09;是计算机科学中数值数据类型的一类值&#xff0c;表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。 EEE 75…

Linux Mii management/mdio子系统分析之六 fixed-mii_bus分析(mac2mac分析)

&#xff08;转载&#xff09;原文链接&#xff1a;[https://blog.csdn.net/u014044624/article/details/130674908] (https://blog.csdn.net/u014044624/article/details/130674908) 前面几章我们介绍了MDIO模块的大部分内容&#xff0c;针对mii_bus、mdio_bus、phy_device、p…

学习鸿蒙先解决这几个是关键问题~

HarmonyOS 是最近最火的操作系统&#xff0c;HarmonyOS 宣布删除 Android 代码之后&#xff0c;正式向世界上第三大操作系统有迈进了一步&#xff0c;HarmonyOS 前期为了完成从 Android 到 HarmonyOS 的过渡&#xff0c;在设计之初 HarmonyOS 采用了双框架架构设计。 从图中可以…

【栈】【字符串和int类型转化】Leetcode 150 逆波兰表达式求值

【栈】【字符串和int类型转化】Leetcode 150 逆波兰表达式求值 解法1 栈 ---------------&#x1f388;&#x1f388;题目链接 Leetcode 150 逆波兰表达式求值 &#x1f388;&#x1f388;------------------- 解法1 栈 字符串转化为int类型数据: Integer.parseInt(s) Long.p…

SpringBoot教程(十五) | SpringBoot集成RabbitMq

SpringBoot教程(十五) | SpringBoot集成RabbitMq RabbitMq是我们在开发过程中经常会使用的一种消息队列。今天我们来研究研究rabbitMq的使用。 rabbitMq的官网&#xff1a; rabbitmq.com/ rabbitMq的安装这里先略过&#xff0c;因为我尝试了几次都失败了&#xff0c;后面等我…

FPGA时序分析与时序约束(四)——时序例外约束

目录 一、时序例外约束 1.1 为什么需要时序例外约束 1.2 时序例外约束分类 二、多周期约束 2.1 多周期约束语法 2.2 同频同相时钟的多周期约束 2.3 同频异相时钟的多周期约束 2.4 慢时钟域到快时钟域的多周期约束 2.5 快时钟域到慢时钟域的多周期约束 三、虚假路径约…

网站SEO优化方案

1&#xff0c;去各类搜索引擎里面&#xff0c;注册你的站点 解决方案&#xff1a;注册地址&#xff1a;https://seo.chinaz.com/chinaz.com 2&#xff0c;网站地址使用 https 会增加搜索排名 解决方案&#xff1a;https:www.xxx.com 3&#xff0c;官网每个页面的 meta 里面&a…

牛客周赛 Round 10 解题报告 | 珂学家 | 三分模板 + 计数DFS + 回文中心扩展

前言 整体评价 T2真是一个折磨人的小妖精&#xff0c;写了两版DFS&#xff0c;第二版计数DFS才过。T3是三分模板&#xff0c;感觉也可以求导数。T4的数据规模才n1000&#xff0c;因此中心扩展的 O ( n 2 ) O(n^2) O(n2)当仁不让。 A. 游游的最长稳定子数组 滑窗经典题 从某个…

78、avx2 数据 load/store 向量化操作介绍

向量寄存器和一个最简单的寄存器-内存的存储器模型,查看上一节。 本节基于整个内存模型,介绍一下如何使用 avx2 向量指令集,来完成数据从内存到寄存器中的交互的。 load 操作 在改内存模型下,load 操作指将数据从内存中加载到寄存器中。 使用 C++ 代码实现如下: float…

REVIT二次开发修改轴网

REVIT二次开发修改轴网 步骤1 步骤2 步骤3 功能实现在这 using System; using System.Collections.Generic; using System.Linq; using

【实操】基于 GitHub Pages + Hexo 搭建个人博客

《开发工具系列》 【实操】基于 GitHub Pages Hexo 搭建个人博客 一、引言二、接入 Node.js2.1 下载并安装 Node.js2.2 环境变量配置 三、接入 Git3.1 下载并安装 Git3.2 环境变量配置 四、接入 Hexo4.1 安装 Hexo4.2 建站4.3 本地启动服务器 五、接入 GitHub Pages5.1 初识 G…

大模型学习与实践笔记(七)

一、环境配置 1.平台&#xff1a; Ubuntu Anaconda CUDA/CUDNN 8GB nvidia显卡 2.安装 # 构建虚拟环境 conda create --name xtuner0.1.9 python3.10 -y # 拉取 0.1.9 的版本源码 git clone -b v0.1.9 https://github.com/InternLM/xtuner# 从源码安装 XTuner pip insta…

【Proteus仿真】【Arduino单片机】汽车车窗除霜系统设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用LCD1602显示模块、光线传感器、DS18B20温度传感器、PCF8691 ADC模块、继电器加热模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD…

常考SQL

1 思维导图 2 题目 mysql8版本 1. 连续问题♥♥♥ 问题描述&#xff1a;如下数据为蚂蚁森林中用户领取的减少碳排放量&#xff0c;找出连续3天及以上减少碳排量在100以上的用户。 iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021…

pyqtgraph绘图类

pyqtgraph绘图类 pyqtgraph绘图有四种方法: 方法描述pyqtgraph.plot()创建一个新的QWindow用来绘制数据PlotWidget.plot()在已存在的QWidget上绘制数据PlotItem.plot()在已存在的QWidget上绘制数据GraphicsLayout.addPlot()在网格布局中添加一个绘图 上面四个方法都接收同样…