(开源)QT | 多线程串口助手

QT|使用多线程编写一个简易版串口助手

运行效果如下图:
在这里插入图片描述

serialport.h

#ifndef SERIALPORT_H
#define SERIALPORT_H

#ifndef COMSERVICE_H
#define COMSERVICE_H

#include <QThread>
#include <QObject>
#include <QSerialPort>


class ComSerialPort : public QObject
{
    Q_OBJECT
public:
    explicit ComSerialPort(QString PortName,qint32 BaudRate,qint32* state,QObject *parent = nullptr);
    ~ComSerialPort();
    bool InitSerial(QString portname,qint32 BaudRate);
    void CloseSerial();

signals:
    void UpdateData(QByteArray data);

public slots:
    void RcvData();
    void SendSerialData(QByteArray data);

private:
    QSerialPort *Serial;
    QThread* thread;
};

#endif // COMSERVICE_H

#endif // SERIALPORT_H

serialport.cpp

#include "serialport.h"
#include <QSerialPortInfo>
#include <QDebug>


ComSerialPort::ComSerialPort(QString PortName,qint32 BaudRate,qint32* state, QObject *parent) : QObject(parent)
{

    thread = new QThread();
    Serial = new QSerialPort();

    *state = InitSerial(PortName,BaudRate);

    this->moveToThread(thread);
    Serial->moveToThread(thread);
    thread->start();

    connect(thread, &QThread::finished, this,&QObject::deleteLater);

}

ComSerialPort::~ComSerialPort()
{

}


bool ComSerialPort::InitSerial(QString portname,qint32 BaudRate)
{
    Serial->setPortName(portname);
    if(!Serial->open(QIODevice::ReadWrite))
    {
        qDebug()<<"串口打开失败";
        return 0;
    }
    qDebug()<<"串口打开成功";
    Serial->setBaudRate(BaudRate);   // 默认115200
    Serial->setDataBits(QSerialPort::Data8);        // 默认
    Serial->setParity(QSerialPort::NoParity);
    Serial->setStopBits(QSerialPort::OneStop);

    // 读取数据
    connect(Serial,&QSerialPort::readyRead,this,&ComSerialPort::RcvData);

    return 1;
}

void ComSerialPort::CloseSerial()
{

    Serial->clear();
    Serial->close();

}

void ComSerialPort::RcvData()
{
    QByteArray buffer = Serial->readAll();
    qDebug() << "接收数据线程ID:"<< QThread::currentThreadId();
    emit  UpdateData(buffer);
}

void ComSerialPort::SendSerialData(QByteArray data)
{
    qDebug() << "发送数据线程ID:"<< QThread::currentThreadId();
    // 接收GUI数据并发送
    Serial->write(data);
    Serial->flush();
    Serial->waitForBytesWritten(10);
}


mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPortInfo>
#include <QString>
#include <QMessageBox>
#include <QDateTime>
#include <QFile>
#include <QFileDialog>
#include <QDebug>
#include <QFileInfo>
#include <QSerialPortInfo>
#include <QtSerialPort/QSerialPort>
#include <QtCharts/QChartView>
#include <QtCharts/QSplineSeries>
#include <QValueAxis>
#include <QThread>
#include "serialport.h"

QT_CHARTS_USE_NAMESPACE

#define MAX_X   50
#define MAX_Y   70
#define TRUE    1
#define FALSE   0
class ComSerialPort;
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
signals:
     void SendData(QByteArray data);
private slots:
    void on_OpenUart_clicked();
    void on_CloseUart_clicked();
    void ClearRecvButton();
    void TimeSendReady(int state);
    void SendDataHex(int state);
    void SaveRecvDataFile();
    void find_port();
    void RcvData(QByteArray RecvBuff);
    void on_SendDataFile_clicked();
    void on_RefreshUart_clicked();

private:
    Ui::MainWindow *ui;
    ComSerialPort *Serial;

    QTimer *timersend;
    QString     SendTextStr;
    QByteArray  SendTextByte;
    quint32     dataRxNumber = 0 ;      // 记录Rx数据量
    quint8      Serial_State = 0 ;      // 串口状态

};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTimer>
#include <QTime>
#include <QDebug>



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QStringList serialNamePort;


    /*定时发送定时器*/
    timersend = new QTimer();
    /*定时器超时信号槽*/
    connect(timersend, SIGNAL(timeout()), this, SLOT(on_SendDataFile_clicked()));
    connect(ui->timesendcheckBox,SIGNAL(stateChanged(int)),this,SLOT(TimeSendReady(int)));
    foreach(const QSerialPortInfo &info , QSerialPortInfo::availablePorts()){
        serialNamePort<<info.portName();
    }
    QFont font("Microsoft YaHei",10,QFont::Normal);//微软雅黑。字体大小16,Normal:正常,Bold 粗体,Black:黑体,Light:高亮
    setWindowTitle("  串口助手 V3.0 ");
    ui->uartbox->addItems(serialNamePort);      // 引入当前串口
    setFixedSize(930,560); // 固定窗口的大小
    ui->CloseUart->setEnabled(false);       // 断开按键关使能
    ui->RecvTextEdit->setFont(font);
    ui->SendTextEdit->setFont(font);
    connect(ui->ClearRecvButton, SIGNAL(clicked()), this, SLOT(ClearRecvButton()));         // 清空接收按钮
    connect(ui->SaveRecvDataFile, SIGNAL(clicked()), this, SLOT(SaveRecvDataFile()));       // 保存数据按钮

}

MainWindow::~MainWindow()
{
    delete ui;
}
/*
    函   数:serialPortReadyRead_Slot
    描   述:接收数据文本框的槽函数
    输   入:无
    输   出:无
*/
void MainWindow::RcvData(QByteArray RecvBuff)
{
    QString     stringdata;

    ui->RecvTextEdit->setTextColor(QColor(Qt::black));
    // 读取串口接收的数据
    if(ui->recvcheckBox->isChecked()){
        stringdata = RecvBuff.toHex(' ').trimmed().toUpper();/*hex显示*/
    }
    else{
       stringdata = QString(RecvBuff);   /*ascii显示*/
    }

    /*时间戳按钮*/
    if (ui->timecheckBox->isChecked()){
       stringdata = QString("[%1]:RX -> %2").arg(QTime::currentTime().toString("HH:mm:ss:zzz")).arg(stringdata);
       ui->RecvTextEdit->append(stringdata);
    }
    else{
        ui->RecvTextEdit->insertPlainText(stringdata);
    }
    ui->RecvTextEdit->moveCursor(QTextCursor::End);  // 自动滚屏到最后一行 有BUG 不可用光标点击文本框
    dataRxNumber += RecvBuff.length();
    ui->RxNumlabel->setText(QString::number(dataRxNumber));
    RecvBuff.clear();
}

/*
    函   数:on_OpenUart_clicked
    描   述:打开并设置串口的信号槽函数
    输   入:无
    输   出:无
*/
void MainWindow::on_OpenUart_clicked()
{
    qint32 state = 0 ;

    Serial   = new ComSerialPort(ui->uartbox->currentText(),ui->baudbox->currentText().toInt(),&state);

    qDebug() << "主线程ID:"<< QThread::currentThreadId();
    if(state){              // 打开串口成功
        ui->OpenUart->setEnabled(false);
        ui->CloseUart->setEnabled(true);
        Serial_State = TRUE;
        QMessageBox::warning(this,tr("提示"),tr("串口连接成功"));
        // 获取串口数据
        connect(this,&MainWindow::SendData,Serial,&ComSerialPort::SendSerialData,Qt::AutoConnection);
        connect(Serial,SIGNAL(UpdateData(QByteArray)),this,SLOT(RcvData(QByteArray)),Qt::AutoConnection);
      }
    else{
        QMessageBox::warning(this,tr("错误"),tr("串口连接失败"));
    }
}

void MainWindow::on_CloseUart_clicked()
{
    Serial->deleteLater();
    Serial_State = 0;
    ui->OpenUart->setEnabled(true);     // 连接串口按键使能
    ui->CloseUart->setEnabled(false);   // 断开按键关使能
}
/*
    函   数:find_port
    描   述:查找串口
    输   入:无
    输   出:无
*/
void MainWindow::find_port()
{
    ui->uartbox->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
        QSerialPort serial;
        serial.setPort(info);   //设置串口
        if(serial.open(QIODevice::ReadWrite)){
            ui->uartbox->addItem(serial.portName());        //显示串口名称
            serial.close();
        }
    }
}

void MainWindow::ClearRecvButton()
{
    ui->RecvTextEdit->clear();
    dataRxNumber = 0 ;
    ui->RxNumlabel->setText(QString::number(dataRxNumber));
}
/*
    函   数:SaveRecvDataFile
    描   述:保存数据按钮点击槽函数
    输   入:无
    输   出:无
*/
void MainWindow::SaveRecvDataFile()
{
    QString data;

    data = ui->RecvTextEdit->toPlainText();

    if (data.isEmpty()){
        QMessageBox::information(this, "提示", "数据内容空");
        return;
    }
    QString curPath = QDir::currentPath();            //获取系统当前目录
    QString dlgTitle = "保存文件";                     //对话框标题
    QString filter = "文本文件(*.txt);;所有文件(*.*)";  //文件过滤器
    QString filename = QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);
    if (filename.isEmpty()){
        return;
    }
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly)){
        return;
    }
    /*保存文件*/
    QTextStream stream(&file);
    stream << data;
    file.close();
}

/*
    函   数:on_SendDataFile_clicked
    描   述:手动发送数据
    输   入:无
    输   出:无
*/
void MainWindow::on_SendDataFile_clicked()
{

    if (Serial_State == TRUE){
        //获取发送框字符
        SendTextStr = ui->SendTextEdit->document()->toPlainText();
        SendTextByte = SendTextStr.toUtf8();
        if (SendTextByte.isEmpty() != true){

            if (ui->HexsendcheckBox->isChecked()) {
                SendTextByte = SendTextByte.fromHex(SendTextByte);
                emit SendData(SendTextByte);
                QString strdata = SendTextByte.toHex(' ').trimmed().toUpper();
                if (ui->timecheckBox->isChecked()) { // 时间戳发送
                    ui->RecvTextEdit->setTextColor(QColor(Qt::blue));  // 时间戳颜色
                    ui->RecvTextEdit->append(QString("[%1]TX -> ").arg(QTime::currentTime().toString("HH:mm:ss:zzz")));
                }
                else{
                  strdata = strdata.append("\r\n");
                }
                ui->RecvTextEdit->setTextColor(QColor(Qt::black));
                ui->RecvTextEdit->insertPlainText(strdata);
            }
            else{
                // 发送ascii数据
                emit SendData(SendTextByte);
                QString strdata = QString(SendTextByte);
                if (ui->timecheckBox->isChecked()) { // 时间戳发送
                    ui->RecvTextEdit->setTextColor(QColor(Qt::red)); // 时间戳颜色
                    ui->RecvTextEdit->append(QString("[%1]TX -> ").arg(QTime::currentTime().toString("HH:mm:ss:zzz")));
                }
                else{
                   strdata = strdata.append("\r\n");
                }
                ui->RecvTextEdit->setTextColor(QColor(Qt::black));
                ui->RecvTextEdit->insertPlainText(strdata);
            }
            //移动光标到末尾
            ui->RecvTextEdit->moveCursor(QTextCursor::End);
        }
        else{
            QMessageBox::warning(this, "警告", "您需要在发送编辑框中输入要发送的数据");
        }

    }
    else{
        QMessageBox::information(this, "警告", "串口未打开");
    }
}
/*
    函   数:TimeSendReady
    描   述:定时发送信号槽函数
    输   入:无
    输   出:无
*/
void MainWindow::TimeSendReady(int state)
{
    int settime;

    if (FALSE == Serial_State){
        QMessageBox::information(this, "提示", "串口未打开");
        return;
    }
    /*判断是否有数据*/
    if (ui->SendTextEdit->document()->isEmpty() == true){
        if (ui->timesendcheckBox->isChecked()){
            QMessageBox::warning(this, "警告", "您需要在发送编辑框中输入要发送的数据");
        }
        return;
    }
    /*判断勾选状态*/
    if (state == Qt::Checked){
        /*获取设定时间*/
        settime = ui->timelineEdit->text().toInt();
        if (settime > 0) {
            timersend->start(settime);
        } else {
            QMessageBox::warning(this, "警告", "时间必须大于0");
        }
        ui->timelineEdit->setEnabled(false);
    }
    else{
        /*停止发送*/
        timersend->stop();
        ui->timelineEdit->setEnabled(true);
    }
}
void MainWindow::SendDataHex(int state)
{
    //获取发送框字符
    SendTextStr = ui->SendTextEdit->document()->toPlainText();
    SendTextByte = SendTextStr.toUtf8();
    if (SendTextStr.isEmpty()){
        return;
    }
    //asccii与hex转换
    if (state == Qt::Checked){
        //转换成16进制数并转换为大写
        SendTextByte = SendTextByte.toHex(' ').toUpper();
        ui->SendTextEdit->document()->setPlainText(SendTextByte);
    }
    else{
        //从QByteArray转换为QString
        SendTextStr = SendTextByte.fromHex(SendTextByte);
        ui->SendTextEdit->document()->setPlainText(SendTextStr);
    }
}

void MainWindow::on_RefreshUart_clicked()
{
    ui->uartbox->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
        QSerialPort serial;
        serial.setPort(info);   //设置串口
        if(serial.open(QIODevice::ReadWrite)){
            ui->uartbox->addItem(serial.portName());        //显示串口名称
            serial.close();
        }
    }
}

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: QT5是一种常见的跨平台应用程序开发工具,可以帮助开发者快速开发各种软件。串口调试助手是一种开发工具,可以帮助工程师快速调试串口设备,检测串口通信是否正常。QT5开发串口调试助手的开放源代码,可以让更多的工程师使用这个工具,快速完成串口调试的任务。 QT5开发串口调试助手可以通过读取串口设备的数据,分析设备发送的信息,输出一些特定的信息进行串口调试。它可以设置串口的通信参数,比如波特率、数据位、停止位等等,方便用户根据自己的需求进行配置。用户可以输入数据来进行测试,也可以从串口设备读取数据,进行分析和操作。 QT5开发串口调试助手还可以支持多种操作系统平台,比如Windows、Linux等等,同时还可以支持多种编程语言,比如C++、Java等等。因此,它非常适合用于嵌入式系统的开发和调试。 总之,QT5开发串口调试助手是一种非常实用的工具,它可以有效地帮助工程师快速进行串口调试,提高开发效率。开放源代码使它更具有灵活性和可扩展性,更容易被广泛应用和改进。 ### 回答2: Qt5开发串口调试助手是一款开源的串口调试工具,其优秀的特点主要体现于以下几个方面: 首先,Qt5开发串口调试助手具有易用性。Qt5开发平台为该软件提供良好的图形界面,操作简洁方便。用户可以很容易地通过该软件发现自己需要的串口,进行连接和调试。 其次,Qt5开发串口调试助手具有高效的性能。该工具使用Qt5的自定义串口模块进行串口配置和数据传输,具有非常高的上限速率和数据吞吐量,能够满足大多数串口通信应用需求。 再次,Qt5开发串口调试助手是一个免费开源软件。其开源项目源代码可以公开访问,方便用户进行修改和二次开发。该工具的开源授权许可为LGPLv3和GPLv2,可以保障代码的免费开放性。 除了以上特点,Qt5开发串口调试助手还附带了一些其他实用的功能,例如数据统计、图形绘制、设备控制等。综合来看,Qt5开发串口调试助手是一个值得用户信赖的开源工具,其高效、易用和免费开源等优点,让其在开发者中拥有广泛的欢迎度和使用价值。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值