Qt的信号槽机制学习一

news2024/11/6 21:28:49

一、Qt理论知识简记

(一)信号与槽[1]

        信号与槽是Qt编程的基础,其使得处理界面上各个组件的交互操作变得比较直观和简单,GUI(Graphical User Interface)程序设计的主要工作就是对界面上各组件的信号进行相应。信号(signal)是在特定情况下被发射的通知;槽(slot)是对信号进行响应的函数,槽函数与一般的C++函数一样,可以具有任何参数,也可以被直接调用。

        信号与槽关联是基于函数QObject::connect()实现的,其可以通过SIGNAL/SLOT(Qt的宏)表现,也可以通过函数指针表现,具体示例如下:

# SIGNAL/SLOT(Qt宏)形式
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
一般用法如下:
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));

# 函数指针形式(信号名称唯一,不存在参数不同的其它同名信号)
QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection);
一般用法示例如下:
connect(sender, &sender::signal, receiver, &receiver::slot);

        在使用信号与槽时,需要注意的规则:

1. 在使用信号与槽的类中,必须在类的定义中插入宏Q_OBJECT;

2. 当信号和槽函数带有参数时,在函数connect()里要指明各参数的类型,但不用指明参数名称;

3. 当一个信号与多个槽函数关联时,槽函数按照建立连接时的顺序依次进行;

4. 多个信号可以连接同一个槽函数;

5. 一个信号可以连接另一个信号;

6. 当一个信号被发射时,与其关联的槽函数通常被立即执行。只有当信号关联的所有槽函数运行完毕后,才运行发射信号处后面的代码

(二)元对象系统[1][4][5]

        Qt中引入了元对象系统(Meta-Object System)对标准C++语言进行扩展,增加了信号与槽、属性系统、动态翻译等特性,为编写GUI应用程序提供了极大的方便。Qt的元对象系统的功能建立在以下三个方面:

1. QObject类是所有使用元对象系统的类的基类;

2. 必须在一个类的开头部分插入宏Q_OBJECT,这样这个类才可以使用元对象系统的特性;

3. 在构建项目时,MOC会读取C++源文件,当它发现类的定义里有Q_OBJECT宏时,它就会为这个类生成另一个包含元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被标准C++编译器编译和链接(MOC为每个QObject的子类提供必要的代码来实现元对象系统的特性)。

       元对象系统的特性是通过QObject的一些函数来实现的。元对象系统的特性包括元对象(meta-Object)、类型信息、动态翻译、对象树(Object-Tree)、信号与槽和属性系统(类的定义代码中需要用宏Q_PROPERTY定义属性)。

        由于C++的RTTI机制只能提供有限的类型信息,于是Qt构建了自己的元对象系统,并基于元对象信息实现了强大的信号槽机制[4]

简略补充说明(详细可见参考资料[1]-P48): 

        对象树。使用QObject及其子类创建的对象(统称为QObject对象)是以对象树的形式来组织的。创建一个QObject对象时若设置一个父对象,它就会被添加到父对象的子对象列表里。一个父对象被删除时,其全部子对象就会被自动删除。QObject类的构造函数里有一个参数parent,用于设置对象的父对象。

        元对象。每个QObject及其子类的实例都有一个自动创建的元对象,元对象是QMetaObject类型的实例。元对象存储了类的实例所属类的各种元数据,包括类信息元数据、方法元数据、属性元数据等。所以,元对象实质上是对类的描述

        通过使用QObject和QMetaObject提供的接口函数,可以在运行时获得一个对象的类名称以及其父类的名称,判断其是否从某个类继承而来。由于这个元对象系统的特性,实现运行时类型信息获取并不需要C++编译器的运行时类型信息(run-time type information,RTTI)支持。

二、Qt信号槽机制下的信号与信号槽的连接方式

        枚举类型Qt::ConnectionType包含四个值,分别为Qt::AutoConnection(默认值)Qt::DirectConnectionQt::QueuedConnectionQt::BlockingQueuedConnection

        Qt::AutoConnection(默认值),如果信号的接收者与发射者在同一个线程中,就使用Qt::DirectConnection方式,否则使用Qt::QueuedConnection方式,在信号发射时自动确定关联方式;

        Qt::DirectConnection,信号被发射时槽函数立即运行,槽函数与信号在同一个线程中;

        Qt::QueuedConnection,在事件循环回到接收者线程后运行槽函数,槽函数与信号在不同的线程中;

        Qt::BlockingQueuedConnection,与Qt::QueuedConnection相似,区别是信号线程会阻塞,直到槽函数运行完毕。当信号与槽函数在同一个线程中时绝对不能使用这种方式,否则会造成死锁。

图1 QT信号槽机制下的信号与信号槽的连接方式

三、带参信号传递与带参槽函数接收

(一)信号与槽位于同一个线程

        1、参数为Qt元对象系统中的基础类型

// mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void emitQString(QString,int);

public slots:
    void showText(QString,int);

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H


// mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this, [=]()
    {
        emit emitQString("My age is",18);
    });
    connect(this,SIGNAL(emitQString(QString,int)),this,SLOT(showText(QString, int)));
}

MainWindow::~MainWindow()
{
    delete ui;
}



void MainWindow::showText(QString, int)
{
    int age = 18;
    ui->label->setText("My age is"+QString::number(age));
}


// main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
图2 信号与槽位于相同线程的Qt基础类型参数传递实验结果

        2、参数为自定义类型(Qt元对象系统不能自然识别)

// mainwindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

struct MyStruct
{
    MyStruct::MyStruct(QString aa,int bb,int *cc):a(aa),b(bb),c(cc){};
    QString a;
    int b;
    int *c;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void emitQString(MyStruct);

public slots:
    void showText(MyStruct);

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H


// mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this, [=]()
    {
        int *a = new int(12);
        MyStruct s = MyStruct("My age is",6,a);
        emit emitQString(s);
        delete a;
    });
    connect(this,SIGNAL(emitQString(MyStruct)),this,SLOT(showText(MyStruct)));
}

MainWindow::~MainWindow()
{
    delete ui;
}



void MainWindow::showText(MyStruct s)
{
    int age = s.b + *(s.c);
    ui->label->setText(s.a+QString::number(age));
}



// main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
图3 信号与槽位于相同线程的自定义结构体类型参数传递实验结果

(二)信号与槽位于不同线程[2]

        1、参数为Qt元对象系统中的基础类型

// MyThread_1.h

#ifndef MYTHREAD_1_H
#define MYTHREAD_1_H

#include <QThread>

class MyThread_1:public QThread
{
    Q_OBJECT

public:
    explicit MyThread_1(QObject *parent = nullptr);

protected:
    void run();

signals:
    void emitString(QString, int);
};

#endif // MYTHREAD_1_H


// MyThread_1.cpp

#include "MyThread_1.h"

MyThread_1::MyThread_1(QObject *parent) : QThread(parent)
{

}

void MyThread_1::run()
{
    int age = 18;
    QString a = "My age is";
    emit emitString(a,age);
}


// mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:


public slots:
    void showText(QString,int);

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H



// mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "MyThread_0.h"
#include "MyThread_1.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    MyThread_1* subThread = new MyThread_1;
    connect(ui->pushButton_2,&QPushButton::clicked, this, [=]()
    {
        subThread->start();
    });
                
connect(subThread,SIGNAL(emitString(QString,int)),this,SLOT(showText(QString,int)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::showText(QString text,int age)
{
    ui->label_2->setText(text+QString::number(age));
}


// main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

图4 信号与槽位于不同线程的Qt基础类型参数传递实验结果

        2、参数为自定义类型(Qt元对象系统不能自然识别)

        2.1 未注册元对象类型直接使用

        由图5可知,自定义结构体类型无法直接作为信号参数传递到位于不同线程中的槽函数中,不同线程间的信号和槽通过Qt::QueuedConnection类型连接。程序报错为“QObject::connect: Cannot queue arguments of type 'MyStruct'(Make sure 'MyStruct' is registered using qRegisterMetaType().)”。

图5 信号与槽位于不同线程的自定义结构体类型参数传递实验结果

        2.2 注册元对象类型使用

        在注册元对象类型时,首先需要在自定义结构体定义后接上“Q_DECLARE_METATYPE(自定义结构体名称);”,然后在该信号connect前加入“int id = qRegisterMetaType<自定义结构体名称>();”以完成元对象类型注册。

        需要注意的是,自定义结构体中需要有合适的默认构造函数可用。一般而言不重新定义构造函数,即使用默认构造函数。

图6  Qt Assistant示例(源自参考资料[2])
// MyThread_1.h

#ifndef MYTHREAD_1_H
#define MYTHREAD_1_H

#include <QThread>

struct MyStruct
{
    QString a;
    int b;
    int *c;
};
Q_DECLARE_METATYPE(MyStruct);

class MyThread_1:public QThread
{
    Q_OBJECT

public:
    explicit MyThread_1(QObject *parent = nullptr);

protected:
    void run();

signals:
    void emitString(MyStruct);
};

#endif // MYTHREAD_1_H


// MyThread_1.cpp

#include "MyThread_1.h"

MyThread_1::MyThread_1(QObject *parent) : QThread(parent)
{

}

void MyThread_1::run()
{
    int *a = new int(12);
    MyStruct s;
    s.a = "my age is";
    s.b = 6;
    s.c = a;
    emit emitString(s);
    delete a;
}


// mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <MyThread_1.h>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:


public slots:
    void showText(MyStruct s);

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

// mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "MyThread_1.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    MyThread_1* subThread = new MyThread_1;
    connect(ui->pushButton_2,&QPushButton::clicked, this, [=]()
    {
        subThread->start();
    });
    int id = qRegisterMetaType<MyStruct>();
    connect(subThread,SIGNAL(emitString(MyStruct)),this,SLOT(showText(MyStruct)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::showText(MyStruct s)
{
    int age = s.b + *(s.c);
    ui->label_2->setText(s.a+QString::number(age));
}


// main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
图7 信号与槽位于不同线程的自定义结构体类型参数传递实验结果

参考资料:

[1] Qt 6 C++开发指南 / 王维波,栗宝鹃,侯春望著.—北京:人民邮电出版社,2023.1.

[2] Assistant 5.15.2 (MSVC 2019 64-bit)-Qt::ConnectionType(Qt使用文档助手)

[3] 《QT从基础到进阶·三》信号槽关联类型Connection,DirectConnection,QueuedConnection,BlockingQueuedConnection-阿里云开发者社区 (aliyun.com)

[4] Qt中的元对象系统(Meta-Object System) - 知乎 (zhihu.com)

[5] 【Qt 元对象系统04】 深入浅出Qt的QMetaObject:探索元对象的魔法-阿里云开发者社区 (aliyun.com)

[6] 元对象系统 |Qt 核心 6.8.0

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

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

相关文章

P11232 [CSP-S 2024] 超速检测

P11232 [CSP-S 2024] 超速检测 难度&#xff1a;普及/提高。 考点&#xff1a;二分、贪心。 题意&#xff1a; 题意较长&#xff0c;没有题目大意&#xff0c;否则你也大意。 主干道长度为 L L L&#xff0c;有 n n n 辆车&#xff0c;看做左端点为 0 0 0&#xff0c;第 …

使用GetX实现GetPage中间件

前言 GetX 中间件&#xff08;Middleware&#xff09;是 GetX 框架中的一种机制&#xff0c;用于在页面导航时对用户进行权限控制、数据预加载、页面访问条件设置等。通过使用中间件&#xff0c;可以有效地控制用户的访问流程&#xff0c;并在适当条件下引导用户到所需页面。 这…

JAVA:常见 JSON 库的技术详解

1、简述 在现代应用开发中&#xff0c;JSON&#xff08;JavaScript Object Notation&#xff09;已成为数据交换的标准格式。Java 提供了多种方式将对象转换为 JSON 或从 JSON 转换为对象&#xff0c;常见的库包括 Jackson、Gson 和 org.json。本文将介绍几种常用的 JSON 处理…

视频怎么去水印?7个视频去水印在线工具大比拼,宝藏工具推荐!

您是否正在寻找一款好用的视频去水印在线工具&#xff0c;却总是难以找到合适的去水印软件&#xff1f;别担心&#xff0c;今天在本文中小编将和大家分享一些去水印的小助手。很多人都觉得视频或图片上的水印十分烦人。如果您有着同样的烦恼&#xff0c;那么使用去水印工具将是…

如何一键更换ppt模板?掌握这2个ppt技巧快速搞定!

每当要制作ppt&#xff0c;很多人会第一时间去搜刮各种ppt模板&#xff0c;有时我们找到了一份貌似符合需求的模板&#xff0c;等到了ppt制作环节&#xff0c;才发现离我们的预期相距甚远&#xff0c;做到一半的ppt如何换模板呢&#xff1f; 想要在中途更换ppt模板&#xff0c;…

0基础入门linux文件系统

目录 文件系统简介 1. 文件系统类型 2. 文件系统结构 3. 文件系统的主要功能 4. 文件系统的使用 5. 文件系统的维护 6. 注意事项 简单举例 机械硬盘 物理结构介绍​编辑 CHS寻址 逻辑结构介绍 LBA寻址法 文件系统与磁盘管理 Boot Block Data block inode block…

2024年meme币走势分析:最后两个月的市场趋势与机会 #交易所#dapp#KOL社区合作

2024年即将步入尾声&#xff0c;meme币市场经历了显著的波动。对于加密市场来说&#xff0c;年底的走势尤为关键&#xff0c;尤其是meme币这种受市场情绪影响较大的加密资产。本文将从市场环境、宏观经济因素、投资者情绪、技术分析等方面分析meme币在2024年最后两个月的潜在走…

Cpp多态机制的深入理解(20)

文章目录 前言一、多态的概念二、多态的定义与实现两个必要条件虚函数虚函数的重写重写的三个例外override 和 final重载、重写(覆盖)、重定义(隐藏) 三、抽象类概念接口继承和实现继承 四、多态的原理虚表和虚表指针虚函数调用过程动态绑定与静态绑定 五、那...那单继承甚至多…

.net core 接口,动态接收各类型请求的参数

[HttpPost] public async Task<IActionResult> testpost([FromForm] object info) { //Postman工具测试结果&#xff1a; //FromBody,Postman的body只有rawjson时才进的来 //参数为空时&#xff0c;Body(form-data、x-www-form-urlencoded)解析到的数据也有所…

高清解压视频素材从哪儿下载?推荐5个高清推文素材资源网站

做抖音小说推文&#xff0c;哪里找爆款素材&#xff1f;作为一名从业多年的视频剪辑师&#xff0c;今天就来给大家揭秘可以下载爆款推文视频素材的网站&#xff0c;如果你也在苦苦找寻找&#xff0c;赶紧进来看看吧&#xff5e; 1. 稻虎网 首先是稻虎网&#xff0c;作为国内顶…

Python的全局锁GIL解析

Python的全局锁&#xff08;GIL&#xff09;是 CPython 解释器实现中的一个机制&#xff0c;用来确保任何时候只有一个线程执行 Python 字节码。这一机制存在于 CPython 中&#xff0c;主要是为了确保线程操作中的数据一致性&#xff0c;但也因此限制了多线程的并行执行效率。尤…

ELK的ElasticStack概念

目录 传送门前言一、ElasticStack是什么二、ElasticStack数据格式1、Elasticsearch的概述2、Elasticsearch核心概念&#xff08;1&#xff09;接近实时&#xff08;NRT&#xff09;&#xff08;2&#xff09;集群&#xff08;cluster&#xff09;&#xff08;3&#xff09;节点…

硅谷甄选(9)SKU模块

SKU模块 8.1 SKU静态 <template><el-card><el-table border style"margin: 10px 0px"><el-table-column type"index" label"序号" width"80px"></el-table-column><el-table-columnlabel"名称…

如何将ppt转换成word文档?8款ppt转word免费的软件大揭秘,值得收藏!

在日常办公中&#xff0c;将ppt转换成word文档的需求日益增长。这种需求源自多个方面&#xff0c;比如制作详尽的报告、创建课程讲义&#xff0c;或者将信息转化为可编辑的格式。作为一种普遍使用的演示工具&#xff0c;ppt在许多商业环境中扮演着重要角色。然而&#xff0c;随…

pandas——DataFrame

一、dataframe &#xff08;一&#xff09;创建dataframe file.csv Name,Age,City Alice,30,New York Bob,25,Los Angeles Charlie,35,Chicagoimport pandas as pd 1.使用字典创建DataFrame&#xff1a; 其中字典的键是列名&#xff0c;值是数据列表。print(1.使用字典创建D…

vxe-table v4.8+ 与 v3.10+ 虚拟滚动支持动态行高,虚拟渲染更快了

Vxe UI vue vxe-table v4.8 与 v3.10 解决了老版本虚拟滚动不支持动态行高的问题&#xff0c;重构了虚拟渲染&#xff0c;渲染性能大幅提升了&#xff0c;行高自适应和列宽拖动都支持&#xff0c;大幅降低虚拟渲染过程中的滚动白屏&#xff0c;大量数据列表滚动更加流畅。 自适…

关于武汉芯景科技有限公司的马达驱动芯片AT6237开发指南(兼容DRV8837)

一、芯片引脚介绍 1.芯片引脚 二、系统结构图 三、功能描述 逻辑功能

青出于“蓝”的合资第一新能源,“换壳”背后有门道

文/王俣祺 导语&#xff1a;千呼万唤始出来的新能源“马6”终于亮相了&#xff0c;这款马自达EZ-6本以为凭借马自达多年来在国内市场深耕的底蕴可以收获一片支持&#xff0c;但最近却深陷“换壳”风波。那么今天我们就一起看看&#xff0c;这款马自达EZ-6和被冠以“原型”的深蓝…

Github上的十大RAG(信息检索增强生成)框架

信息检索增强生成(Retrieval-Augmented Generation,简称RAG)是一种强大的技术,能够显著提升大型语言模型的性能。RAG框架巧妙地结合了基于检索的系统和生成模型的优势,可以生成更加准确、符合上下文、实时更新的响应。随着对先进人工智能解决方案需求的不断增长,GitHub上涌现出…

【小白学机器学习28】 统计学脉络+ 总体+ 随机抽样方法

目录 参考书&#xff0c;学习书 0 统计学知识大致脉络 1 个体---抽样---整体 1.1 关于个体---抽样---整体&#xff0c;这个三段式关系 1.2 要明白&#xff0c;自然界的整体/母体是不可能被全部认识的 1.2.1 不要较真&#xff0c;如果是人为定义的一个整体&#xff0c;是可…