【QT问题解决】QT Modbus rtu 拖动主界面时modbus的槽函数无法响应的解决方案

目录

  • 问题 Modbus放在主线程,界面事件会阻塞信号传输
  • 解决方案 将modbus放在子线程实现

问题 Modbus放在主线程,界面事件会阻塞信号传输

在使用QT5.14.2时 使用QT自带的QModbusClient类实现对一个力传感器的数据读取。本人为了测试就将modbus读取逻辑等都写在主线程中,但是本人采用modbus异步通讯的方式,给从站发一个读取数据的信号(sendReadRequest),然后使用信号槽等待从站回复数据(readSerialForceData)。这种方式的好处是不会阻塞主线程。

但是!本人发现一个问题,就是在我拖动主界面的时候,modbus通讯就停止了!当我松开鼠标的时候,modbus正常通讯!目前还不知道为什么会这样,猜测是鼠标事件影响了modbus的信号槽机制。但是我试了一下,其他的槽函数都是能正常触发的,比如定时器之类。在我拖动界面的时候,UI界面也可以正常的刷新。只有modbus被阻塞了。

void MainWindow::on_readRequest()
{// 读取寄存器QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters,0,1);if (auto *reply = modbusDevice->sendReadRequest(readUnit, 1)){if (!reply->isFinished())connect(reply, &QModbusReply::finished, this, &MainWindow::readSerialForceData);elsedelete reply; // broadcast replies return immediately}else{//底部状态栏显示ui->statusBar->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);}
}
void MainWindow::readSerialForceData()
{auto reply = qobject_cast<QModbusReply *>(sender());if (!reply)return;if (reply->error() == QModbusDevice::NoError){const QModbusDataUnit unit = reply->result();//遍历数据单元的值for (int i = 0, total = int(unit.valueCount()); i < total; ++i){qint16 sValue = static_cast<qint16>(unit.value(i));forceData_Temp = sValue/10.0;const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i).arg(QString::number(sValue));ui->textBrowser_Log->append(entry);}}else if (reply->error() == QModbusDevice::ProtocolError){statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16), 5000);}else{statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").arg(reply->errorString()).arg(reply->error(), -1, 16), 5000);}reply->deleteLater();
}

解决方案 将modbus放在子线程实现

经过测试,发现将modbus放在子线程中,通过信号槽与主线程进行通讯,将数据保存成静态变量或者全局变量,即可解决问题。

这里新建一个serialThread 类用
.h文件

class serialThread : public QObject
{Q_OBJECT
public:serialThread(QObject *parent = Q_NULLPTR);~serialThread();QModbusClient *modbusDevice = nullptr;QDateTime startTime;QDateTime endTime;double intervalTime = 0;//串口数据间隔时间double intervalTime_Last=0;//数据采集定时器QTimer *pollTimer;//串口数据读取定时器signals:void sig_connectSerialFinished(bool flag);void sig_disconnectSerialFinished(bool flag);public slots:void startThread();void on_connectSerial();void on_disconnectSerial();void on_startReadData();void on_stopReadData();void on_readRequest();void on_readRequestFinished();
};#endif // SERIALTHREAD_H

.cpp文件

#include "serialthread.h"serialThread::serialThread(QObject *parent ) : QObject(parent)
{qDebug()<<"串口线程构造函数:"<<QThread::currentThreadId();
}serialThread::~serialThread()
{//退出串口if (modbusDevice){modbusDevice->disconnectDevice();}delete modbusDevice;}void serialThread::startThread()
{qDebug()<<"串口线程成员函数 startThread :"<<QThread::currentThreadId();modbusDevice = new QModbusRtuSerialMaster(this);
}void serialThread::on_connectSerial()
{qDebug()<<"串口线程成员函数 on_connectSerial :"<<QThread::currentThreadId();QMutexLocker locker(&ForceSensorGlobal::mutex);  // 锁定互斥锁//设置串口号modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,ForceSensorGlobal::SeriComName);//设置波特率modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,ForceSensorGlobal::BaudRate);//设置数据位modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,ForceSensorGlobal::DataBits);//设置奇偶检验switch(ForceSensorGlobal::ParityID){case 0: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity);break;case 1: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::EvenParity);break;case 2: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::OddParity);break;case 3: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::SpaceParity);break;case 4: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::MarkParity);break;default: break;}//设置停止位switch(ForceSensorGlobal::StopBitsID){case 0: modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop);break;case 1: modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneAndHalfStop);break;case 2: modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::TwoStop);break;default: break;}//设置执行请求时间modbusDevice->setTimeout(1000);//设置执行请求次数modbusDevice->setNumberOfRetries(3);//连接串口 并发送成功与否标志emit sig_connectSerialFinished(modbusDevice->connectDevice());pollTimer = new QTimer;pollTimer->setTimerType(Qt::PreciseTimer);//精确定时pollTimer->setInterval(ForceSensorGlobal::IntervalTime);connect(pollTimer,&QTimer::timeout, this, &serialThread::on_readRequest);}void serialThread::on_disconnectSerial()
{modbusDevice->disconnectDevice();emit sig_disconnectSerialFinished(true);}void serialThread::on_startReadData()
{QMutexLocker locker(&ForceSensorGlobal::mutex);  // 锁定互斥锁//清空一下数据 方便再次存储ForceSensorGlobal:: recordData.clear();QVector<double> vectorTemp;for (int i = 0; i < 2; i++){ForceSensorGlobal::recordData.append(vectorTemp);}startTime = QDateTime::currentDateTime();//获取开始时间pollTimer->setInterval(ForceSensorGlobal::IntervalTime);pollTimer->start();qDebug() <<"子线程 on_startReadData ";
}void serialThread::on_stopReadData()
{pollTimer->stop();qDebug() <<"子线程 on_stopReadData ";
}void serialThread::on_readRequest()
{// 读取寄存器QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters,0,1);if (auto *reply = modbusDevice->sendReadRequest(readUnit, 1)){if (!reply->isFinished())connect(reply, &QModbusReply::finished, this, &serialThread::on_readRequestFinished);elsereply->deleteLater(); // broadcast replies return immediately}else{//读取错误qDebug() <<"读取错误: " <<modbusDevice->errorString();}
}void serialThread::on_readRequestFinished()
{auto reply = qobject_cast<QModbusReply *>(sender());if (!reply)return;QMutexLocker locker(&ForceSensorGlobal::mutex);  // 锁定互斥锁if (reply->error() == QModbusDevice::NoError){const QModbusDataUnit unit = reply->result();//遍历数据单元的值for (int i = 0, total = int(unit.valueCount()); i < total; ++i){//接受信号的时间endTime = QDateTime::currentDateTime();intervalTime = startTime.msecsTo(endTime) / 1000.0;double now = (intervalTime - intervalTime_Last)*1000.0;qDebug() <<now;intervalTime_Last = intervalTime;//收到的数据是uint16 需转换为int16qint16 sValue = static_cast<qint16>(unit.value(i));//传感器默认有一位小数,需除以10double forceData_Temp = sValue/10.0;//将时间 力数据存在到全局变量中ForceSensorGlobal::recordData[0].append(intervalTime);ForceSensorGlobal::recordData[1].append(forceData_Temp);}}else if (reply->error() == QModbusDevice::ProtocolError){//接收到的响应信息是协议错误qDebug() <<"接收到的响应信息是协议错误: " <<modbusDevice->errorString();}else{//接收到的响应信息是其他错误qDebug() <<"接收到的响应信息是其他错误: " <<modbusDevice->errorString();}reply->deleteLater();
}

在主线程中通过moveToThread,将modbus放入子线程

主界面.h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QSerialPort>
#include <QtSerialBus>
#include <QModbusDataUnit>
#include <QModbusClient>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QDebug>
#include <QSettings>
#include "forcesensorglobal.h"
#include "qcustomplot.h"
#include "CurvePlot.h"
#include "serialthread.h"namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();/*************串口通讯线程***************///串口线程QThread * mySerialThread;//串口操作类serialThread  *mySerialModbus;/***************定时器**********************/QTimer *plotTimer;////UI界面初始化void initUI();//搜索串口void SearchSerialPorts();signals:void sig_connectSerial();void sig_disconnectSerial();void sig_startReadData();void sig_stopReadData();private slots:void on_plotTimerTimeOut();void on_pBtn_connectSerial_clicked();void on_pBtn_disconnectSerial_clicked();//接受子线程 modbus串口连接完成信号void on_connectSerialFinished(bool flag);//接受子线程 modbus串口断开完成信号void on_disconnectSerialFinished(bool flag);void on_pBtn_refreshSerial_clicked();void on_pBtn_startRead_clicked();void on_pBtn_stopRead_clicked();void on_pBtn_sendData_clicked();
private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

主界面.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);initUI();/*创建串口线程*/mySerialModbus = new serialThread();mySerialThread = new QThread;mySerialModbus->moveToThread(mySerialThread);//线程开始mySerialThread->start();connect(mySerialThread, &QThread::started, mySerialModbus, &serialThread::startThread);connect(mySerialThread, &QThread::finished, mySerialModbus, &QObject::deleteLater);//终止线程时要调用deleteLater槽函数connect(mySerialThread, &QThread::finished, mySerialModbus, &QObject::deleteLater);/*绘图刷新定时器*/plotTimer = new QTimer;plotTimer->setInterval(100);connect(plotTimer,&QTimer::timeout, this, &MainWindow::on_plotTimerTimeOut);/*****************信号槽*********************************///主线->子线程 串口连接信号connect(this, &MainWindow::sig_connectSerial, mySerialModbus, &serialThread::on_connectSerial);//主线->子线程 串口断开信号connect(this, &MainWindow::sig_disconnectSerial, mySerialModbus, &serialThread::on_disconnectSerial);//主线->子线程 开始采集信号connect(this, &MainWindow::sig_startReadData, mySerialModbus, &serialThread::on_startReadData);//主线->子线程 停止采集信号connect(this, &MainWindow::sig_stopReadData, mySerialModbus, &serialThread::on_stopReadData);//子线程->主线 串口连接完成信号connect(mySerialModbus, &serialThread::sig_connectSerialFinished,this,&MainWindow::on_connectSerialFinished);//子线程->主线 串口断开完成信号connect(mySerialModbus, &serialThread::sig_disconnectSerialFinished,this,&MainWindow::on_disconnectSerialFinished);void MainWindow::on_pBtn_connectSerial_clicked()
{//更新数据ForceSensorGlobal::SeriComName = ui->cBox_SeriComName->currentText();ForceSensorGlobal::BaudRate = ui->cBox_BaudRate->currentText().toInt();ForceSensorGlobal::DataBits = ui->cBox_DataBits->currentText().toInt();ForceSensorGlobal::ParityID = ui->cBox_Parity->currentIndex();ForceSensorGlobal::StopBitsID = ui->cBox_StopBits->currentIndex();ForceSensorGlobal::IntervalTime = ui->sBox_Interval->value();emit sig_connectSerial();qDebug()<<"主线程 sig_connectSerial :"<<QThread::currentThreadId();}void MainWindow::on_pBtn_disconnectSerial_clicked()
{on_pBtn_stopRead_clicked();emit sig_disconnectSerial();
}void MainWindow::on_connectSerialFinished(bool flag)
{if (flag){// 设置控件可否使用ui->pBtn_connectSerial->setEnabled(false);ui->pBtn_refreshSerial->setEnabled(false);ui->pBtn_disconnectSerial->setEnabled(true);}else    //打开失败提示{QMessageBox::information(this,tr("错误"),tr("连接从站失败!"),QMessageBox::Ok);}
}void MainWindow::on_disconnectSerialFinished(bool flag)
{if (flag){// 设置控件可否使用ui->pBtn_connectSerial->setEnabled(true);ui->pBtn_refreshSerial->setEnabled(true);ui->pBtn_disconnectSerial->setEnabled(false);}else{QMessageBox::information(this,tr("错误"),tr("断开连接失败!"),QMessageBox::Ok);}}void MainWindow::on_pBtn_refreshSerial_clicked()
{//填充串口号组合框SearchSerialPorts();
}void MainWindow::on_pBtn_startRead_clicked()
{ForceSensorGlobal::IntervalTime = ui->sBox_Interval->value();emit sig_startReadData();plotTimer->start();}void MainWindow::on_pBtn_stopRead_clicked()
{plotTimer->stop();emit sig_stopReadData();}}

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

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

相关文章

各种编码格式和网站工具

1、url编码 格式如&#xff1a; 中文 > %E4%B8%AD%E6%96%87 hello > hello [] > %5B%5D 在线工具&#xff1a;https://www.bejson.com/enc/urlencode/ 2、utf8编码 格式如&#xff1a; 中文 > \xe4\xb8\xad\xe6\x96\x87 hello > \x68\x65\x6c\x6c\x6f [] >…

基于深度学习的向量图预测

基于深度学习的向量图预测 向量图预测&#xff08;Vector Graphics Prediction&#xff09;是计算机视觉和图形学中的一个新兴任务&#xff0c;旨在从像素图像&#xff08;栅格图像&#xff09;生成相应的向量图像。向量图像由几何图形&#xff08;如线条、曲线、多边形等&…

【网络安全产品】---网闸

了解了不少安全产品&#xff0c;但是对网闸的理解一直比较模糊&#xff0c;今天 what 网闸是安全隔离与信息交换系统的简称&#xff0c;使得在不影响数据正常通信的前提下&#xff0c;让络在不连通的情况下数据的安全交换和资源共享&#xff0c;对不同安全域/网络之间实现真正…

u8g2 使用IIC驱动uc1617 lcd有时候某些像素显示不正确

折腾了很久&#xff0c;本来lcd是挂载到已经存在的iic总线上的&#xff0c;总线原来是工作正常的&#xff0c;挂载之后lcd也能显示&#xff0c;但是有时候显示不正确&#xff0c;有时候全白的时候有黑色的杂点。 解决方案&#xff1a; 1.最开始以为是IIC总线速度快&#xff0…

【机器学习】从理论到实践:决策树算法在机器学习中的应用与实现

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f4d5;引言 ⛓决策树的基本原理 1. 决策树的结构 2. 信息增益 熵的计算公式 信息增益的计算公式 3. 基尼指数 4. 决策树的构建 &#x1f916;决策树的代码实现 1. 数据准备 2. 决策树模型训练 3.…

Vim基础操作:常用命令、安装插件、在VS Code中使用Vim及解决Vim编辑键盘错乱

Vim模式 普通模式&#xff08;Normal Mode&#xff09;&#xff1a; 这是 Vim 的默认模式&#xff0c;用于执行文本编辑命令&#xff0c;如复制、粘贴、删除等。在此模式下&#xff0c;你可以使用各种 Vim 命令来操作文本。插入模式&#xff08;Insert Mode&#xff09;&#…

【Python日志模块全面指南】:记录每一行代码的呼吸,掌握应用程序的脉搏

文章目录 &#x1f680;一、了解日志&#x1f308;二、日志作用&#x1f308;三、了解日志模块⭐四、日志级别&#x1f4a5;五、记录日志-基础❤️六、记录日志-处理器handler&#x1f3ac;七、记录日志-格式化记录☔八、记录日志-配置logger&#x1f44a;九、流程梳理 &#x…

如何在linux中下载R或者更新R

一、问题阐述 package ‘Seurat’ was built under R version 4.3.3Loading required package: SeuratObject Error: This is R 4.0.4, package ‘SeuratObject’ needs > 4.1.0 当你在rstudio中出现这样的报错时&#xff0c;意味着你需要更新你的R 的版本了。 二、解决方…

Openldap集成Kerberos

文章目录 一、背景二、Openldap集成Kerberos2.1kerberos服务器中绑定Ldap服务器2.1.1创建LDAP管理员用户2.1.2添加principal2.1.3生成keytab文件2.1.4赋予keytab文件权限2.1.5验证keytab文件2.1.6增加KRB5_KTNAME配置 2.2Ldap服务器中绑定kerberos服务器2.2.1生成LDAP数据库Roo…

第二十五篇——信息加密:韦小宝说谎的秘诀

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 加密这件事&#xff0c;对于这个时代的我们来说非常重要&#xff0c;那么…

ElasticSearch学习笔记(二)文档操作、RestHighLevelClient的使用

文章目录 前言3 文档操作3.1 新增文档3.2 查询文档3.3 修改文档3.3.1 全量修改3.3.2 增量修改 3.4 删除文档 4 RestAPI4.1 创建数据库和表4.2 创建项目4.3 mapping映射分析4.4 初始化客户端4.5 创建索引库4.6 判断索引库是否存在4.7 删除索引库 5 RestClient操作文档5.1 准备工…

Android RecyclerView原理语法和用法

RecyclerView 是 Android 中用于显示大量数据的高级视图组件。它是 ListView 的改进版&#xff0c;具有更高的性能和灵活性。以下是关于 RecyclerView 的详细介绍&#xff0c;包括其原理、语法、优缺点和使用方法。 原理 RecyclerView 的核心原理是通过视图复用来提高性能。当…

什么是Java中的单例模式?请列举几种常见的单例实现方式,并讨论其优缺点。请解释Java中的设计模式,并列举几种常见的设计模式及其应用场景。

什么是Java中的单例模式&#xff1f;请列举几种常见的单例实现方式&#xff0c;并讨论其优缺点。 Java中的单例模式是一种创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。这种模式在多线程环境中特别有用&#xff0c;因…

融资A轮B轮是什么意思?

环境&#xff1a; 融资 问题描述&#xff1a; 融资A轮B轮是什么意思 解决方案&#xff1a; 在A轮融资之前&#xff0c;通常有以下几轮融资阶段&#xff1a; 种子轮&#xff08;Seed Round&#xff09;&#xff1a;这是企业生命周期中最早的融资阶段&#xff0c;通常发生在…

模拟原神圣遗物系统-小森设计项目,设计圣遗物(生之花,死之羽,时之沙,空之杯,理之冠)抽象类

分析圣遗物 在圣遗物系统&#xff0c;玩家操控的是圣遗物的部分 因此我们应该 物以类聚 人与群分把每个圣遗物的部分&#xff0c;抽象出来 拿 生之花&#xff0c;死之羽为例 若是抽象 类很好的扩展 添加冒险家的生之花 时候继承生之花 并且名称冒险者- 生之花 当然圣遗物包含…

求职难遇理想offer!!【送源码】

现在的求职行情确实不太好&#xff0c;有很多抱怨自己找到的工作技术栈落后的同学&#xff0c;我也是建议他们接下先干着。不能幻想毕业之后还能找到更合适的工作&#xff0c;那个时候就基本只能参加社招了&#xff0c;没有工作经验参加社招想要获得满意 offer 的更是地狱难度。…

【尚庭公寓SpringBoot + Vue 项目实战】移动端登录管理(二十)

【尚庭公寓SpringBoot Vue 项目实战】移动端登录管理&#xff08;二十&#xff09; 文章目录 【尚庭公寓SpringBoot Vue 项目实战】移动端登录管理&#xff08;二十&#xff09;1、登录业务2、接口开发2.1、获取短信验证码2.2、登录和注册接口2.3、查询登录用户的个人信息 1、…

【BES2500x系列 -- RTX5操作系统】CMSIS-RTOS RTX -- 实时操作系统的核心,为嵌入式系统注入活力 --(一)

&#x1f48c; 所属专栏&#xff1a;【BES2500x系列】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f49…

动态住宅代理IP:多账号矩阵管理的使用

如果您要处理多个在线帐户&#xff0c;选择正确的代理类型对于实现流畅的性能至关重要。但最适合这项工作的代理类型是什么&#xff1f; 为了更好地管理不同平台上的多个账户并优化成本&#xff0c;动态住宅代理IP通常作用在此。 一、什么是轮换代理&#xff1f; 轮换代理充当…

数据仓库与数据库的区别

在数据管理和分析的过程中&#xff0c;我们常常会听到“数据库”和“数据仓库”这两个术语。 虽然它们看起来相似&#xff0c;但实际上它们在设计目的、结构和使用场景上都有显著的区别。 数据库是什么&#xff1f; 数据库&#xff08;Database&#xff09;是一个用于存储和管…