QT多线程接收串口数据

**

QT多线程接收串口数据

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

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

3.软件测试效果

QT多线程接收串口数据_第1张图片
QT多线程接收串口数据_第2张图片

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);
}

你可能感兴趣的:(物联网,qt,串口通信,多线程)