QT 使用线程完成串口数据的读写,并把数据传递给主线程

news2024/11/18 13:56:40

1、之前做串口通信的时候直接在主线程 用了api实现,这种只做个串口助手是OK的,但是在大型项目中这样做会占用主线程的时间,数据量大的时候不确定有多卡顿。

2、看了网上很多资料,没一个写的是对的,主要错在 QSerialPort()在主线程里面创建,因为QIODevice的子类都不应跨线程操作。不然会有这种报错 QObject: Cannot create children for a parent that is in a different thread.

3、花了几天时间,实现开一个线程完成串口数据的读写,并把数据传递给主线程,主线程界面UI不会被阻塞。可以把这个线程完美嵌入到各个大型项目。

4、主要思路如下:

4.1、写个类用来操作QSerialPort,类成员里放一个QSerialPort指针,然后把对外交互的函数都写成槽,再写一个creatSerialPort完成指针的实例化和参数初始化。

4.2、然后,在构造这个类的地方,也构造一个QThread,将类对象moveToThread(thread),thread->start(),将thread的started信号绑定到obj的init函数上,把QSerialPort的readyRead信号绑到这个obj的槽上。槽里调用read,然后把读的结果用信号emit到外部。写操作,在obj里提供一个write写槽函数内直接调用串口类的write。外部使用时,emit一个信号,该信号触发obj的write槽函数。

4.3 需要在子线程分配的资源,比如QSerialPort,全部在该object的某个槽函数(如init)中进行,将该槽函数绑定到线程的started信号上
4.4、程序退出时,不要直接delete object,因为那个对象不处于子线程。在需要退出时,执行object的deleteLater函数(直接调用或通过信号槽触发均可),这样就会由它所属的线程负责delete这个object。然后将object的destroyed信号,绑定到线程的quit槽上,将线程的finished信号绑定到QThread对象的deleteLater槽上。这样,销毁流程就是->子线程删除object->线程停止->线程对象销毁
4.5、线程对象,以及move到线程里的对象,都不要设置parent
4.6、Qt 4.8之后,可以把线程的finish信号直接绑到对象的deleteLater上,QThread会保证在子线程中删除这些对象。

5、部分代码如下(完整代码已嵌入目前项目,已测试稳定,不崩溃,因为花了挺久时间,不无偿公开。

思路可以无偿公开:如上面第四条描述)

serialportservice.h
class SerialPortService : public QObject
{
    Q_OBJECT
public:
    explicit SerialPortService(QObject *parent = nullptr);
    ~SerialPortService();

    void setSerialPara(QString name,QSerialPort::BaudRate br,QSerialPort::DataBits dn,
         QSerialPort::StopBits sn,QSerialPort::Parity pr,QSerialPort::FlowControl fl);
public slots:
    void creatSerialThread();
    void creatSerialPort();

    void readData();  //通过串口读数据
    void writeData(QByteArray data); //通过串口写数据

    void destorySerialThread();
    void closeSerialThread();

signals:

  //发送数据到其它线程
  void sendData(QByteArray data);

private:
//  QThread *serialThread;
  QSerialPort *serialPort;


  QString portName;
  QSerialPort::BaudRate baudRate;
  QSerialPort::DataBits dlen;
  QSerialPort::StopBits slen;
  QSerialPort::Parity par;
  QSerialPort::FlowControl fctrl;
};

serialtestpage.cpp

#include "serialtestpage.h"

SerialTestPage::SerialTestPage(QWidget *parent) : QWidget(parent)
{
    setAttribute(Qt::WA_DeleteOnClose);
    dataInit();
    layoutInit();
    scanSerialPort();

    /* 波特率初始化 */
    baudRateItemInit();
    /* 数据位项初始化 */
    dataBitsItemInit();
    /* 检验位项初始化 */
    parityItemInit();
    /* 停止位项初始化 */
    stopBitsItemInit();
    /*流控方式*/
    flowControlInit();

    initSerialPortService();
}
void SerialTestPage::receiveSerialData(QByteArray data)
{
   qDebug() << "SerialTestPage::receiveSerialData currentThreadId:" << 
   QThread::currentThreadId();
   if(data.size() >= 1){
       qDebug()<<"receiveSerialData tmpdata = " << data;
   }
   displaySerialData(data);
}

void SerialTestPage::threadFinished()
{
    qDebug()<<"SerialTestPage threadFinished"<<QThread::currentThreadId();
//    serialPortService->deleteLater();

}

void SerialTestPage::serialDestory()
{
    qDebug()<<"SerialTestPage serialDestory"<<QThread::currentThreadId();
    serialThread->quit();
    serialThread->wait();
}

void SerialTestPage::openSerialPortPushButtonClicked()
{
    if(pushButton[1]->text() == "打开串口"){
        if(comboBox[0]->currentText().isEmpty()){
            qDebug() << "isEmpty ********";
            QMessageBox::about(NULL, "错误","为空!可能串口已经被占用!");
            return;
        }

        if(comboBox[0]->currentText().isNull()){
            qDebug() << "isNull ********";
            QMessageBox::about(NULL, "错误","isNull!可能串口已经被占用!");
            return;
        }

        serialPortService->setSerialPara(comboBox[0]->currentText(),getBaud(),getDataBits(),getStopBits(),getParity(),getFlowControl());

        emit sendStartSerialThread();

        for (int i = 0; i < FUNCTION_LENGTH; i++){
            comboBox[i]->setEnabled(false);
        }
        pushButton[1]->setText("关闭串口");
        pushButton[0]->setEnabled(true);
    }else{
        for (int i = 0; i < FUNCTION_LENGTH; i++){
            comboBox[i]->setEnabled(true);
        }

//        emit sendDestorySerialThread();
        emit sendCloseSerialThread();

        if(serialThread->isRunning()){
            qDebug()<<"********** serialThread()->isRunning()";
        }
        pushButton[1]->setText("打开串口");
        pushButton[0]->setEnabled(false);
    }
    qDebug() << "SerialTestPage::openSerialPortPushButtonClicked:" << QThread::currentThreadId();

}

6、UI如下:

 

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

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

相关文章

Linux-用户及文件权限管理

目录1、Linux 用户管理1.1 查看用户1.2 创建用户1.3 用户组1.4 删除用户和用户组2、Linux文件权限2.1 查看文件权限2.2 变更文件所有者2.3 修改文件权限3、补充1、Linux 用户管理 Linux可以实现多用户登录&#xff0c;A和B用户可以同时登录同一台主机&#xff0c;虽然他们的文…

IT帮助台如何帮助企业进行人力资源管理

在大多数组织中&#xff0c;HR 服务管理流程从内部聊天或电话开始&#xff0c;以 Word 文档和 Excel 工作表结束。以这种老式的方式管理所有这些信息变得非常乏味&#xff0c;因为 组织成长。大多数组织&#xff0c;即使有人力资源管理系统&#xff0c;也使用过时的系统&#x…

2-3-内存泄漏和内存溢出

1、内存泄漏和内存溢出区别 内存泄漏&#xff1a;是指程序在申请内存后&#xff0c;无法释放已申请的内存空间&#xff0c;一次内存泄漏似乎不会有大的影响&#xff0c;但内存泄漏堆积后的后果就是内存溢出。 内存泄漏是指你向系统申请分配内存进行使用(new)&#xff0c;可是…

vue指令

文章目录1.vue基础1.el挂载点2.data数据对象2.vue指令1.v-text2.v-html3.v-on4.v-show5.v-if6.v-bind7.v-for8.v-model1.vue基础 1.el挂载点 在el命中的内部可以无限嵌套el绑定的元素&#xff0c;可以支持id选择器&#xff0c;class选择器&#xff0c;标签选择器。并且双标签…

element - - - - - Form表单的resetFields()方法没有生效?

万事如伊 大吉大利 Form表单的resetFields方法没有生效?1. 场景描述2. 问题分析3. 解决办法关于element组件&#xff0c;相信各位同学都不陌生。其各个组件不可谓不好用&#xff0c;能够快速的帮助开发人员进行排版布局&实现效果。 但是总会遇到一些不可避免的坑。 1. 场…

win10中pycharm使用django问题总结

1.问题一出现问题&#xff1a;ERROR: Could not find a version that satisfies the requirement XXX解决方法直接选用pip源并且信任它的来源就可以解决这种问题。pip install django版本号 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 这里将pip源换成…

公司招了个在腾讯拿35K的人,让我见识到什么才叫测试天花板···

一进公司&#xff0c;看见门外有几个带着行李在外面等待的人&#xff0c;应该就是来入职的&#xff0c;果不其然&#xff0c;在进公司后&#xff0c;召开了新人见面会&#xff0c;让他们做了自我介绍和职业规划以及部门&#xff0c;令我印象最深刻的就是一个刚刚从腾讯离职出来…

【C++修炼之路】类和对象(上)—— 入门篇

&#x1f451;作者主页&#xff1a;安 度 因 &#x1f3e0;学习社区&#xff1a;StackFrame &#x1f4d6;专栏链接&#xff1a;C修炼之路 文章目录一、前言二、初识面向过程和面向对象三、类的引入和定义四、类的访问限定符及封装1、访问限定符2、封装五、类的作用域六、类的实…

Day03-无处不在的数据

文章目录Day03-无处不在的数据无处不在的数据分析案例1&#xff1a;论文选题案例2&#xff1a;产品面试案例3&#xff1a;工作汇报用户的依赖性案例4&#xff1a;选票逻辑几乎所有岗位都需要数据分析Day03-无处不在的数据 昨天&#xff0c;我们为你打开了数据分析的大门&#…

vue生命周期钩子函数

1 vue的生命周期的钩子函数有哪些 beforeCreate &#xff1a;在调用beforeCreate时&#xff0c;已完成的内容是创建了一个空的Vue实例对象&#xff0c;这个对象上有默认的生命周期函数和默认的事件created &#xff1a;此阶段时&#xff0c;数据配置结束。包括数据侦听、计算属…

((蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第2天】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有74天

&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6; 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&a…

【JavaScript速成之路】JavaScript变量

&#x1f4c3;个人主页&#xff1a;「小杨」的csdn博客 &#x1f525;系列专栏&#xff1a;【JavaScript速成之路】 &#x1f433;希望大家多多支持&#x1f970;一起进步呀&#xff01; 文章目录前言1&#xff0c;JavaScript变量1.1&#xff0c;变量的含义1.2&#xff0c;变量…

C语言指针变量的定义和使用

数据在内存中的地址也称为指针&#xff0c;如果一个变量存储了一份数据的指针&#xff0c;我们就称它为指针变量。在C语言中&#xff0c;允许用一个变量来存放指针&#xff0c;这种变量称为指针变量。指针变量的值就是某份数据的地址&#xff0c;这样的一份数据可以是数组、字符…

微盟全链路压测:如何帮助电商业务实现 10 倍性能提升?

一分钟精华速览 全链路压测之所以被誉为电商大促备战的 “核武器” &#xff0c;是因为它基于实际的生产业务场景、系统环境&#xff0c;模拟海量的用户请求和数据对整个业务链进行压力测试&#xff0c;能真实反映系统的状况&#xff0c;对系统风险和瓶颈真正做到心中有数。 …

Flutter 学习 - Dart 语言基础

文章目录前言一、Dart 概述Dart 重要的概念二、变量与基本数据类型三、函数四、运算符五、流程控制语句六、异常处理七、面向对象构造函数继承类抽象类枚举类型八、泛型九、库的使用引用库指定库前缀引用库的一部分总结技巧1. 安全调用2. 设置默认值3. 简化判断前言 Dart 作为…

【RabbitMQ】快速入门学习MQ

目录 1.初识MQ 1.1.同步和异步通讯 1.1.1.同步通讯 1.1.2.异步通讯 1.2.技术对比&#xff1a; 2.快速入门 2.1.安装RabbitMQ 2.2.RabbitMQ消息模型 2.3.导入Demo工程 2.4.入门案例 2.4.1.publisher实现 2.4.2.consumer实现 2.5.总结 1.初识MQ 1.1.同步和异步通讯…

最全面的SpringBoot教程(二)——SpringBoot配置文件

前言 本文为SpringBoot配置文件相关内容介绍&#xff0c;下边将对配置文件分类&#xff0c;yaml基本语法&#xff0c;yaml数据格式&#xff0c;获取数据&#xff0c;profile-运维&#xff0c;项目内部配置文件加载顺序&#xff0c;项目外部配置文件加载顺序等进行详尽介绍~ &a…

Golang实现微信公众号后台

最近在学习Golang&#xff0c;写了个微信公众号项目练练手。 一、开发前准备 1、注册微信公众号 百度搜索微信公众号进入官网&#xff0c;注册一个订阅号&#xff0c;其他信息按要求填写即可。 注册完成后进入个人公众号主页&#xff0c;下拉至设置与开发 点击基本配置&…

π130E31 3.0kV rms 隔离电压200Mbps三通道数字隔离器代替Si8635BC-B-IS1

π130E31 3.0kV rms 隔离电压200Mbps三通道数字隔离器代替Si8635BC-B-IS1&#xff0c;具有出色的性能特 征和可靠性&#xff0c;整体性能优于光耦和基于其他原理的数字隔离器 产品。 智能分压技术(iDivider技术)是荣湃半导体发明的新一代数字 隔离器技术。智能分压技术(iDivide…

【强训】Day08

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录一、选择二、编程&#xff08;比较简单&#xff09;1. 两种排序方法2. 求最小公倍数&#xff08;主要注意辗转相除法就行&#xff09;答案1. 选择2. 编程普通小孩也要热爱生活&#xff01; 一、选择 以下选项中&am…