QT多线程接收串口数据

article/2025/10/27 17:03:55

**

QT多线程接收串口数据

**
1.前言
QT多线程的使用,和绝大数人一样,犯了错误(请查阅Qt开发人员( Bradley T. Hughes)Blog中的文章 you are-doing-it-wrong介绍)。为了解决问题,网上查阅学习了几十篇文章,基本都是错误的使用方法,或者不完整,未能给予正确的引导。
为方便后来学习者,少走弯路,于是自己动手写了一下程序,过程不再赘述,只以完整的案例进行教学,内部注释较多,可供大家阅读、思考。

2.功能作用
使用多线程,避免上位机软件与单片机等硬件设备高速通讯时,造成软件界面假死、丢包等现象。同时对串口进行了简单的封装,方便调用。本文提供了完整的源代码,方便测试。有较详细的注释方便阅读、思考。编译环境为QT5.8.0,Qt Creator4.2.1

3.软件测试效果

在这里插入图片描述
在这里插入图片描述

4.基本步骤
(1)pro文件添加QT5自带的头文件

QT       += serialport

(2)serialworker.h头文件

#include "serialworker.h"SerialWorker::SerialWorker(QSerialPort *ser, QObject *parent) : QObject(parent),serial(ser)
{}
QString SerialWorker::ByteArrayToHexString(QByteArray data)
{QString ret(data.toHex().toUpper());int len = ret.length()/2;qDebug()<<"收到字节长度为:"<<len;for(int i=1;i<len;i++){ret.insert(2*i+i-1," ");}return ret;
}void SerialWorker::doDataReciveWork()
{    QByteArray buffer = serial->readAll();// 2.进行数据处理QString result = ByteArrayToHexString(buffer);qDebug() <<  "子线程收到数据:" << result << "线程ID:" << QThread::currentThreadId();// 3.将结果发送到主线程emit sendResultToGui(result);
}

(3)mainwindow.h文件

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("子线程读串口");this->setMinimumSize(600,248);this->setMaximumSize(1200,496);InitSerialPortName();//1.新建串口处理子线程SerialWorker *ser = new SerialWorker(&serial_1);ser->moveToThread(&serialThread_1);// 2.连接信号和槽QString s;connect(&serialThread_1, &QThread::finished, ser, &QObject::deleteLater);           // 线程结束,自动删除对象connect(&serial_1, &QSerialPort::readyRead, ser, &SerialWorker::doDataReciveWork); // 主线程通知子线程接收数据的信号connect(ser, &SerialWorker::sendResultToGui, this, &MainWindow::handleResults);    // 主线程收到数据结果的信号//  connect(ser,SIGNAL(sendResultToGui(QString)), this, SLOT(handleResults(QString)));     //主线程收到数据结果的信号写法2// 3.开始运行子线程serialThread_1.start();                   // 线程开始运行
}MainWindow::~MainWindow()
{serialThread_1.quit();serialThread_1.wait();delete ui;
}void MainWindow::InitSerialPortName()
{// 清空下拉框ui->box_portName->clear();//通过QSerialPortInfo查找可用串口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){QString showName = info.portName();qDebug() << showName+info.description();ui->box_portName->addItem(showName);}//波特率QStringList baudrateList;baudrateList<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200";ui->box_baudrate->addItems(baudrateList);//添加下拉列表选项ui->box_baudrate->setCurrentText("115200");//界面中初始值//    ui->box_baudrate->setView(new QListView(this));//该设置是配合qss的,不然item行高设置没效果//数据位QStringList databitList;databitList<<"5"<<"6"<<"7"<<"8";ui->box_dataBits->addItems(databitList);ui->box_dataBits->setCurrentText("8");
//      ui->box_dataBits->setView(new QListView(this));//校验位QStringList parityList;parityList<<"无"<<"奇"<<"偶";ui->box_parityBit->addItems(parityList);ui->box_parityBit->setCurrentText("No");
//      ui->box_parityBit->setView(new QListView(this));//停止位QStringList stopbitList;stopbitList<<"1"<<"2";ui->box_stopBit->addItems(stopbitList);ui->box_stopBit->setCurrentText("1");
//      ui->box_stopBit->setView(new QListView(this));//流控制
//        QStringList flowctrlList;
//        flowctrlList<<"No"<<"Hardware"<<"Software";
//        ui->boxFlowControl->addItems(flowctrlList);
//        ui->boxFlowControl->setCurrentText("No");
      ui->boxFlowControl->setView(new QListView(this));
}void MainWindow::on_btn_openPort_clicked()
{if(ui->btn_openPort->text()==QString("打开串口")){//设置串口名QString portName = (ui->box_portName->currentText()).split(":").at(0);qDebug() <<"当前打开串口为:"<<portName;serial_1.setPortName(portName);//设置波特率serial_1.setBaudRate(ui->box_baudrate->currentText().toInt());//设置停止位if(ui->box_stopBit->currentText() == "1")serial_1.setStopBits(QSerialPort::OneStop);else if(ui->box_stopBit->currentText() == "2")serial_1.setStopBits(QSerialPort::TwoStop);//设置数据位数if(ui->box_dataBits->currentText() == "8")serial_1.setDataBits(QSerialPort::Data8);else if(ui->box_dataBits->currentText() == "7")serial_1.setDataBits(QSerialPort::Data7);else if(ui->box_dataBits->currentText() == "6")serial_1.setDataBits(QSerialPort::Data6);else if(ui->box_dataBits->currentText() == "5")serial_1.setDataBits(QSerialPort::Data5);//设置奇偶校验if(ui->box_parityBit->currentText() == "无")serial_1.setParity(QSerialPort::NoParity);else if(ui->box_parityBit->currentText() == "偶")serial_1.setParity(QSerialPort::EvenParity);else if(ui->box_parityBit->currentText() == "奇")serial_1.setParity(QSerialPort::OddParity);//        //设置流控制
//        serial_1.setFlowControl(QSerialPort::NoFlowControl);//打开串口if(!serial_1.open(QIODevice::ReadWrite)){QMessageBox::about(NULL, "提示", "无法打开串口!");return;}//下拉菜单控件失能ui->box_portName->setEnabled(false);ui->box_baudrate->setEnabled(false);ui->box_dataBits->setEnabled(false);ui->box_parityBit->setEnabled(false);ui->box_stopBit->setEnabled(false);ui->btn_openPort->setText(QString("关闭串口"));}else{//关闭串口serial_1.close();//下拉菜单控件使能ui->box_portName->setEnabled(true);ui->box_baudrate->setEnabled(true);ui->box_dataBits->setEnabled(true);ui->box_parityBit->setEnabled(true);ui->box_stopBit->setEnabled(true);ui->btn_openPort->setText(QString("打开串口"));}
}void MainWindow::on_btn_clearText_clicked()
{ui->browser_dataReceive->clear();
}void MainWindow::handleResults(const QString &result)
{qDebug() <<  "主线程收到结果数据:" << result << "线程ID:" << QThread::currentThreadId();//从界面中读取以前收到的数据QString oldString = ui->browser_dataReceive->toPlainText()+'\n';oldString = oldString + QString(result);//清空以前的显示ui->browser_dataReceive->clear();//重新显示ui->browser_dataReceive->append(oldString);
}

(4)mainwindow.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("子线程读串口");this->setMinimumSize(600,248);this->setMaximumSize(1200,496);InitSerialPortName();//1.新建串口处理子线程SerialWorker *ser = new SerialWorker(&serial_1);ser->moveToThread(&serialThread_1);// 2.连接信号和槽QString s;connect(&serialThread_1, &QThread::finished, ser, &QObject::deleteLater);           // 线程结束,自动删除对象connect(&serial_1, &QSerialPort::readyRead, ser, &SerialWorker::doDataReciveWork); // 主线程通知子线程接收数据的信号connect(ser, &SerialWorker::sendResultToGui, this, &MainWindow::handleResults);    // 主线程收到数据结果的信号//  connect(ser,SIGNAL(sendResultToGui(QString)), this, SLOT(handleResults(QString)));     //主线程收到数据结果的信号写法2// 3.开始运行子线程serialThread_1.start();                   // 线程开始运行
}MainWindow::~MainWindow()
{serialThread_1.quit();serialThread_1.wait();delete ui;
}void MainWindow::InitSerialPortName()
{// 清空下拉框ui->box_portName->clear();//通过QSerialPortInfo查找可用串口foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){QString showName = info.portName();qDebug() << showName+info.description();ui->box_portName->addItem(showName);}//波特率QStringList baudrateList;baudrateList<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200";ui->box_baudrate->addItems(baudrateList);//添加下拉列表选项ui->box_baudrate->setCurrentText("115200");//界面中初始值//    ui->box_baudrate->setView(new QListView(this));//该设置是配合qss的,不然item行高设置没效果//数据位QStringList databitList;databitList<<"5"<<"6"<<"7"<<"8";ui->box_dataBits->addItems(databitList);ui->box_dataBits->setCurrentText("8");
//      ui->box_dataBits->setView(new QListView(this));//校验位QStringList parityList;parityList<<"无"<<"奇"<<"偶";ui->box_parityBit->addItems(parityList);ui->box_parityBit->setCurrentText("No");
//      ui->box_parityBit->setView(new QListView(this));//停止位QStringList stopbitList;stopbitList<<"1"<<"2";ui->box_stopBit->addItems(stopbitList);ui->box_stopBit->setCurrentText("1");
//      ui->box_stopBit->setView(new QListView(this));//流控制
//        QStringList flowctrlList;
//        flowctrlList<<"No"<<"Hardware"<<"Software";
//        ui->boxFlowControl->addItems(flowctrlList);
//        ui->boxFlowControl->setCurrentText("No");
      ui->boxFlowControl->setView(new QListView(this));
}void MainWindow::on_btn_openPort_clicked()
{if(ui->btn_openPort->text()==QString("打开串口")){//设置串口名QString portName = (ui->box_portName->currentText()).split(":").at(0);qDebug() <<"当前打开串口为:"<<portName;serial_1.setPortName(portName);//设置波特率serial_1.setBaudRate(ui->box_baudrate->currentText().toInt());//设置停止位if(ui->box_stopBit->currentText() == "1")serial_1.setStopBits(QSerialPort::OneStop);else if(ui->box_stopBit->currentText() == "2")serial_1.setStopBits(QSerialPort::TwoStop);//设置数据位数if(ui->box_dataBits->currentText() == "8")serial_1.setDataBits(QSerialPort::Data8);else if(ui->box_dataBits->currentText() == "7")serial_1.setDataBits(QSerialPort::Data7);else if(ui->box_dataBits->currentText() == "6")serial_1.setDataBits(QSerialPort::Data6);else if(ui->box_dataBits->currentText() == "5")serial_1.setDataBits(QSerialPort::Data5);//设置奇偶校验if(ui->box_parityBit->currentText() == "无")serial_1.setParity(QSerialPort::NoParity);else if(ui->box_parityBit->currentText() == "偶")serial_1.setParity(QSerialPort::EvenParity);else if(ui->box_parityBit->currentText() == "奇")serial_1.setParity(QSerialPort::OddParity);//        //设置流控制
//        serial_1.setFlowControl(QSerialPort::NoFlowControl);//打开串口if(!serial_1.open(QIODevice::ReadWrite)){QMessageBox::about(NULL, "提示", "无法打开串口!");return;}//下拉菜单控件失能ui->box_portName->setEnabled(false);ui->box_baudrate->setEnabled(false);ui->box_dataBits->setEnabled(false);ui->box_parityBit->setEnabled(false);ui->box_stopBit->setEnabled(false);ui->btn_openPort->setText(QString("关闭串口"));}else{//关闭串口serial_1.close();//下拉菜单控件使能ui->box_portName->setEnabled(true);ui->box_baudrate->setEnabled(true);ui->box_dataBits->setEnabled(true);ui->box_parityBit->setEnabled(true);ui->box_stopBit->setEnabled(true);ui->btn_openPort->setText(QString("打开串口"));}
}void MainWindow::on_btn_clearText_clicked()
{ui->browser_dataReceive->clear();
}void MainWindow::handleResults(const QString &result)
{qDebug() <<  "主线程收到结果数据:" << result << "线程ID:" << QThread::currentThreadId();//从界面中读取以前收到的数据QString oldString = ui->browser_dataReceive->toPlainText()+'\n';oldString = oldString + QString(result);//清空以前的显示ui->browser_dataReceive->clear();//重新显示ui->browser_dataReceive->append(oldString);
}

(5)serialworker.cpp文件

#include "serialworker.h"SerialWorker::SerialWorker(QSerialPort *ser, QObject *parent) : QObject(parent),serial(ser)
{}
QString SerialWorker::ByteArrayToHexString(QByteArray data)
{QString ret(data.toHex().toUpper());int len = ret.length()/2;qDebug()<<"收到字节长度为:"<<len;for(int i=1;i<len;i++){ret.insert(2*i+i-1," ");}return ret;
}void SerialWorker::doDataReciveWork()
{    QByteArray buffer = serial->readAll();// 2.进行数据处理QString result = ByteArrayToHexString(buffer);qDebug() <<  "子线程收到数据:" << result << "线程ID:" << QThread::currentThreadId();// 3.将结果发送到主线程emit sendResultToGui(result);
}

http://chatgpt.dhexx.cn/article/lvr39R3W.shtml

相关文章

Qt多线程互斥

目录 一、多线程与临界资源的依赖 现象分析 二、互斥和解决方法 三、QMutex的主要成员函数和使用 四、示例代码 五、小结 一、多线程与临界资源的依赖 除了上一节所说的&#xff0c;多线程在代码执行的时序上会有依赖&#xff0c;那么其他地方是否还有所依赖呢&#xff…

QT 多线程创建方法及应用实例

QT 多线程创建方法及应用实例 方法一&#xff1a; &#xff08;1&#xff09;创建一个QT应用 &#xff08;2&#xff09;创建线程类&#xff0c;继承QThread simplethreadone.h #ifndef SIMPLETHREADONE_H #define SIMPLETHREADONE_H #include <QThread>class SimpleThr…

Qt 多线程专栏

主线程就是进程&#xff0c;进程代表4个G的资源空间&#xff0c;主线程退出就意味着进程退出&#xff0c;在Qt中&#xff0c;主线程不能是子线程的父对象&#xff0c;因此必须在线程的析构函数中去delete线程对象。 只要定时器启动&#xff0c;自动触发timeout()信号 类A继承了…

Qt多线程编程

由于疫情原因被封控在家&#xff0c;相信大家也有跟我类似的情况&#xff0c;在学习知识提升和充实自己&#xff0c;下面我将这几天在学习的Qt多线程编程做个简单的总结&#xff0c;随此笔记的还有几个简单的例子&#xff0c;以便后续开发过程中回忆和参考。 Qt的多线程开发&am…

Qt多线程

Qt多线程有两种方法&#xff1a; 1、定义一个QThread的派生类&#xff0c;重写run函数&#xff0c;run函数中的程序就是新线程中要执行的内容。在主线程中实例化该类&#xff0c;调用start函数&#xff0c;就可以实现多线程。 2、定义一个QObject的派生类&#xff0c;把要在新线…

Qt创建多线程的两种方法

来源&#xff1a;https://github.com/czyt1988/czyBlog/tree/master/tech/QtThread 1.摘要 Qt有两种多线程的方法&#xff0c;其中一种是继承QThread的run函数&#xff0c;另外一种是把一个继承于QObject的类转移到一个Thread里。 Qt4.8之前都是使用继承QThread的run这种方法…

Qt----多线程

文章目录 多线程一、继承QThread的线程1. 应用实例2. 程序运行效果 二、继承QObject的线程1. 应用实例2. 程序运行效果 多线程 我们写的一个应用程序&#xff0c;应用程序跑起来后一般情况下只有一个线程&#xff0c;但是可能也有特殊情况。比如我们前面章节写的例程都跑起来后…

Qt5教程(九):Qt多线程

目录&#xff1a; 一、创建工程 二、QThread 源码一览 三、QThread相关方法介绍 四、创建线程 一、创建工程 先创建一个工程吧, 具体步骤前面讲过很多次了&#xff0c; 就不再细说了。 然后在Header文件夹下创建添加一个头文件, 右键Headers -> Add New... -> C -&g…

Qt 多线程的几种实现方式

Qt多线程的实现方式有&#xff1a; 1. 继承QThread类&#xff0c;重写run()方法 2. 使用moveToThread将一个继承QObject的子类移至线程&#xff0c;内部槽函数均在线程中执行 3. 使用QThreadPool,搭配QRunnable&#xff08;线程池&#xff09; 4. 使用QtConcurrent&#xf…

QT中的多线程

目录 1、QThread介绍 1.2、继承Qthread类 1.2.1、得到线程id 1.2.2、让线程一直执行 1.2.3、线程退出 1.2.4、​​​​​​​connect的第五个参数 1.2.5、线程锁 QMutex ​​​​​​​ 1、QThread介绍 QThread类提供了一个与平台无关的管理线程的方法。一个QThread对…

faild to create process解决办法

win7下运行pip时报faild to create process的解决办法&#xff1a; 1.找到python.exe文件&#xff0c;复制其文件路径。如下图&#xff0c;python.exe路径为D:\interpreter\python.exe。 2.找到pip-script.py文件&#xff0c;打开。将步骤1中复制的路径粘贴在第一行&#xff0…

CreateProcess error=2, 系统找不到指定的文件 解决方法

CreateProcess error2, 系统找不到指定的文件 解决方法 一般这是由于ndk缺少文件引起的 解决方法1&#xff1a; 在项目根目录下的local.properties文件中加cmd后缀 方法二下载16b的版本替换原来的ndk-bundle目录&#xff0c;默认在C:\Users\用户名\AppData\Local\Android\S…

SQL SERVER 2008 执行xp_cmdshell的过程中出错,调用createprocess失败,错误代码 5 解决方案

1、进入 控制面板----管理工具------本地安全策略 点击 本地策略--------安全选项 把“网络安全:LAN管理器身份验证级别”双击打开&#xff0c;选择“发送 LM 和 NTLM 响应”&#xff08;原来是“没有定义”&#xff09;。确认后&#xff0c;重新启动服务器&#xff0c;该问题…

runnerw.exe: CreateProcess failed with error 5:

在idea配置Git时遇到这个问题&#xff1a; 后来发现是我路径设置错了&#xff1a; 解决方案 修改设置里的路径即可&#xff08;Setings-Version Control-Git&#xff09;

Qt creater出现“启动程序失败,路径或者权限错误”或“The process could not be started!”解决方法

Qt creater出现“启动程序失败&#xff0c;路径或者权限错误”或“The process could not be started&#xff01;”解决方法 错误示例 远古版本的QTCreator在编译程序时没问题&#xff0c;在运行程序时会提示“The process could not be started&#xff01;" 较新版本…

failed to create process.

由于python-2.7是先出来&#xff0c;但是官方只更新到2020年1月1日&#xff1b;而python-3.6与python-2.7有一定的差别&#xff0c;会同时在电脑上安装这两个版本的python&#xff0c;为了能更好的调用python不同的版本&#xff0c;会设定一个执行时使用python2.exe&#xff0c…

CreateProcessAsUser

该CreateProcessAsUser函数创建一个新的进程及其主线程。新进程然后执行指定的可执行文件。该CreateProcessAsUser功能类似的CreateProcess函数&#xff0c;除了新进程运行在由hToken参数表示的用户的安全上下文中。默认情况下&#xff0c;新进程是非交互式的&#xff0c;即它运…

生成了文件却还是报错 Error:CreateProcess failed

想起来&#xff0c;以前用keil编译器的时候&#xff0c;也出现过这样的问题&#xff1a; 第一眼都是看到了“1 Error(s)”&#xff0c;就下意识认为自己程序出错了&#xff0c;找了半天没找到。 后来多看了一眼&#xff0c;发现完全这个错误其实可以完全不用理会&#xff0c;因…

【Error】Error running process: CreateProcess failed. Code 2

重新安装了pycham发现在pycharm里打不开控制台窗口了 解决办法&#xff1a; 在file -->setting --> Tools --> Terminal里 把Shell path 从 powershell.exe 改为 cmd.exe

Cannot run program “svn“ (in directory “D:xxxx“): CreateProcess error=2, 系统找不到指定的文件。报错解决方法

目录 一、报错内容 二 、原因 三、解决方法 一、报错内容 Cannot run program "svn" (in directory "xxxx"): CreateProcess error2, 系统找不到指定的文件 二 、原因 TortoiseSVN安装的时候没有安装command命令&#xff0c;不能通过命令执行版本管理…