[C++&Qt] 通过信号与槽传递数据

本文涉及的产品
数据可视化DataV,5个大屏 1个月
可视分析地图(DataV-Atlas),3 个项目,100M 存储空间
简介: [C++&Qt] 通过信号与槽传递数据

一. 前言

Qt视觉项目中,一般需要用到信号和槽机制主要有两个地方:

  1. 软件功能模块Qt界面的各个控件进行交互的时候,需要使用信号和槽机制来完成信息的传递。
  2. 视觉算法模块上位机代码之间的信息传递需要通过信号和槽的机制来完成。

上面两者信号传递的方式本质是一样的,都是通过信号和槽的方式来完成信号的传递与接收,不同点就是前者的Qt控件的触发信号是Qt内部定义好的,我们定义一个槽函数直接用就可以;而后者各个类之间的信息传递是需要我们手动定义信号与槽函数来完成。


二. Qt界面控件信号传递

这里基本上都是和Qt界面上的一些按钮、复选框、编辑框等控件产生的触发信号,可以定义一个槽函数接收信号,也可以直接在connect链接信号与槽时,直接完成信号的接收与处理:

按钮QPushButton

connect(ui.pushButton_compute, &QPushButton::clicked, this, [=]() {
        //槽函数实现
    return;
  }

单选按钮QRadioButton

connect(ui.radioButton_imageLocate, &QRadioButton::toggled, this, [=](bool state) {
    //槽函数实现
    ui.widget_view->clearImage();
    ui.comboBox->clear();    
    if (m_imageViewLocate.size() > 0) {
        for (int index = 0; index < m_imageViewLocate.size(); index++){
        ui.comboBox->addItem(QString::number(index + 1, 10));
        }
        ui.comboBox->setCurrentIndex(m_imageID);
        ui.widget_view->setImage(m_imageViewLocate.at(m_imageID));
    }
});

下拉框QComboBox

connect(ui.cmbAffineType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=](int index) {
    //槽函数实现
  int mFlag = index;
  ui.cmbAffineType->setItemData(1, "v", Qt::UserRole - 1); //设置第二行不可选中
});

计数器QSpinBox

connect(ui->spinBox_colNum, QOverload<int>::of(&QSpinBox::valueChanged),this, [=](int value)
{
    //槽函数实现
    int mFlag = value;
});

浮点计数器QDoubleSpinBox

connect(ui.spbGlueAreaRatio_2, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, [=](double value){
    //槽函数实现
    int mFlag = value;
});

三. 自定义信号传递

下面重点介绍一些第二种信号传递的方式,它涉及到了各个类之间的信号传递与接收

3.1. 信号传递单类型数据(适用于传递的数据较少的情况)

功能需求:在视觉库中通过信号的形式传两个double类型的数据给上位机项目代码

代码逻辑

  • 在基类MU_InspUiBase中定义信号,派生类GetMountOffset的槽函数中发送信号(各个派生类(包含GetMountOffset)继承基类MU_InspUiBase,所以在基类MU_InspUiBase中声明信号就相当于在各个子类中声明)
  • 在看项目代码中各个类之间的包含关系,我这里是基类数据MU_InspUiBase会传递到类MainUI中,再由类MainUI中传递到与软件的接口类VisionUI中,最终上位机软件接收类VisionUI中信号。

这里说的啰嗦了一点主要是想告诉大家,在信号传递之前要搞清楚代码中各个类之间的逻辑关系,才能层层递进完成信号的传递。

具体流程如下:

1、基类MU_InspUiBase中定义信号

class MU_InspUiBase : public QWidget
{
  Q_OBJECT
signals:
    void SIG_calculateOffSetOK(double x, double y);
};

2、基类MU_InspUiBase的派生类GetMountOffset中发送信号

#include "MU_InspUiBase.h"
GetMountOffset::GetMountOffset(const char* _path, QWidget *parent)
  : MU_InspUiBase(parent)
{
    emit SIG_calculateOffSetOK(x, y);
}

3、类MainUI中接收类GetMountOffset发送的信号

#include"MU_InspUiBase.h"
class MF_VISIONUI_EXPORT MainUI : public QWidget
{
  Q_OBJECT
signals:
    void SIG_calculateOffSetOK(double x, double y);
private:  
  MU_InspUiBase *m_InspUi = nullptr;
};
//.cpp
MainUI::MainUI(const char* _filePath, VISIONOPERATETYPE _functionOption, QWidget *parent)
  : QWidget(parent)
{
m_InspUi = new GetMountOffset(_filePath);
connect(m_InspUi, &MU_InspUiBase::SIG_calculateOffSetOK, [this](double x, double y)
{
    emit SIG_calculateOffSetOK(x, y);
});
}

4、软件的接口类VisionUI中重复上面的步骤:定义信号、接收信号、在发送信号,最终由上位机代码中接收该信号,获取其参数值。

注:判断信号有没有发送成功时,可以在connect处加个断点,打印一串代码,看看最终是否打印出来,如:

connect(m_InspUi, &MU_InspUiBase::SIG_calculateOffSetOK, [this](double x, double y)
{
  qDebug() << "yyyyyy";
});

3.2 信号传递自定义结构体数据(适用于传递的数据较多的情况)

1、定义结构体,使用宏Q_DECLARE_METATYPE,向QT声明这个结构体

#include <QVariant>
struct MS_MountOffset_SigResult
{
  MS_MountOffset_SigResult() :m_offsetX(0), m_offsetY(0)
  {}
  double m_offsetX;     //X方向补偿
  double m_offsetY;     //Y方向补偿
};
Q_DECLARE_METATYPE(MS_MountOffset_SigResult);

注:

①宏Q_DECLARE_METATYPE作用:对于自定义的结构体参数,信号槽无法识别参数,导致信号槽连接不起作用。所以需要在结构体中声明结束的地方加上结构体注册。

②自定义类型中一定要有默认构造函数,如果已经显式的定义了带有无默认参数的构造函数则需要另外写上默认构造函数 Test(){}。

③最好在类定义的头文件中加上#include < QVariant>,保证编译器能够识别该宏,否则可能会报错“错误提示: error C4430: 缺少类型说明符 - 假定为 int。”

2、定义信号和槽 — signals&slots

signals:
void SIG_calculateOffSetOK(QVariant VarDetailInfo);
slot:
void slot_calculateOffSetOK(QVariant VarDetailInfo);
QObject::connect(m_InspUi, SIGNAL(&SIG_calculateOffSetOK(QVariant)), this,
      SLOT(&slot_calculateOffSetOK(QVariant)), Qt::QueuedConnection);

3、在发送信号的位置将自定义结构类型封装成QVariant的形式

QVariant m_DataVar;
m_DataVar.setValue(m_MountOffset_SigResult);
emit SIG_calculateOffSetOK(m_DataVar);

注:m_MountOffset_SigResult为定义的结构体MS_MountOffset_SigResult的对象。

4、在接收信号的位置从封装中取出结构体参数

MS_MountOffset_SigResult m_MountOffset_SigResul_2;
m_MountOffset_SigResul_2 = m_DataVar.value<MS_MountOffset_SigResult>();//m_DataVar为上面QVariant的对象

下雨天,最惬意的事莫过于躺在床上静静听雨,雨中入眠,连梦里也长出青苔。


相关实践学习
DataV Board用户界面概览
本实验带领用户熟悉DataV Board这款可视化产品的用户界面
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
目录
相关文章
|
21天前
|
编译器
(9)Qt中信号与槽重载的解决方案
本文介绍了在Qt中处理信号与槽重载问题的三种解决方案:使用函数指针、Qt提供的QOverload类和Qt4的宏方式。
63 3
|
21天前
(8)Qt中的自定义信号
本文介绍了如何在Qt框架中创建和使用自定义信号,并通过一个父子窗口切换的示例来展示自定义信号的实现和应用。
49 3
(8)Qt中的自定义信号
|
22天前
|
Unix Linux C++
Linuxc/c++之信号基础
这篇文章详细介绍了Linux下C/C++信号的基本概念、产生原因、处理过程、分类、注册与发送方法,以及信号屏蔽的机制。
15 0
Linuxc/c++之信号基础
|
3月前
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
121 2
|
3月前
|
存储 算法 C++
C++ STL应用宝典:高效处理数据的艺术与实战技巧大揭秘!
【8月更文挑战第22天】C++ STL(标准模板库)是一组高效的数据结构与算法集合,极大提升编程效率与代码可读性。它包括容器、迭代器、算法等组件。例如,统计文本中单词频率可用`std::map`和`std::ifstream`实现;对数据排序及找极值则可通过`std::vector`结合`std::sort`、`std::min/max_element`完成;而快速查找字符串则适合使用`std::set`配合其内置的`find`方法。这些示例展示了STL的强大功能,有助于编写简洁高效的代码。
43 2
|
3月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
3月前
|
程序员 C++
【Qt】信号与槽(下)
【Qt】信号与槽(下)
|
3月前
|
存储 算法 C++
【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】
【C++】C++ QT实现Huffman编码器与解码器(源码+课程论文+文件)【独一无二】
|
3月前
|
Linux C++
【Qt】信号与槽(上)
【Qt】信号与槽(上)
【Qt】信号与槽(上)
|
3月前
|
存储 安全 数据处理
【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】
【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】