Qt串口助手

QT5 串口助手

​ 由于C++课程作业的需要,用QT5写了个简陋的串口助手。只作为一个简单的案例以供参考,默认读者具有C++基础和了解简单的Qt操作。

功能展示

【用QT写了个简单的串口助手】

准备工作

Qt自带有<QSerialPort> 库, 可以方便地配置和调用计算机的串口资源

将QSerialPort编译到项目中

<QSerialPort> 默认是不能再Qt项目中使用的,需要在.pro文件中 添加一行QT += serialport , 才能使用<QSerialPort>

这点在文档中也有写出

在这里插入图片描述
在.pro文件中添加QT += serialport

在这里插入图片描述

界面设计

在这里插入图片描述

  • 左上文本框显示接收和发送到的信息
  • 右上区域选择串口和对串口进行配置
  • 右边中间打开串口和保存或者清楚接收区的信息,十六进制显示将会使下次接收到的信息以十六进制显示
  • 下边是发送文本区
  • 最下边是定时发送功能,开启后会一直发送信息,能够设置发送的周期

界面初始化

将界面信息和所有配置项加载, 需要代码实现加载的主要有Combox中所有文本, 以及串口的初始配置

加载串口名

/**
* @brief 扫描所有可用的串口,并将其添加到Box中
*/
void CSerial::Scan_Serial(void)
{
    ports = QSerialPortInfo::availablePorts();
    foreach(QSerialPortInfo info, ports)
    {
        qDebug() << "串口名称" << info.portName();
        qDebug() << "设备描述:" << info.description();
        ui->comboBox->addItem(QString(info.portName() + info.description()));
    }
}
  • QSerialPortInfo类,实现了一些静态方法用于获取串口的信息

  • 通过QSerialPortInfo::availablePorts()获取所有可用的串口(以及他们的信息)

  • 然后遍历ports, 将串口名称添加到comboBox中(请忽略qDebug()调试代码)

加载可用波特率、停止位、数据位和检验位配置

与加载串口名类似, 只不过加载的内容是QSerialPort类中定义好的配置, 在注释中也列举出了部分支持的配置(更多支持的配置请查询QSerialPort的文档)

/**
* @brief 将支持的波特率添加到Box中
* 1200 2400 4800 9600 19200 38400 57600 115200
*/
void CSerial::Play_BoudRate(void)
{
    ui->BoudRateBox->addItem("1200");
    ui->BoudRateBox->addItem("2400");
    ui->BoudRateBox->addItem("4800");
    ui->BoudRateBox->addItem("9600");
    ui->BoudRateBox->addItem("19200");
    ui->BoudRateBox->addItem("38400");
    ui->BoudRateBox->addItem("57600");
    ui->BoudRateBox->addItem("115200");
}
/**
* @brief 将停止位添加到Box中
* OneStop 1      TowStop 2     OneAndHalfStop 3
*/
void CSerial::Play_StopBits(void)
{
    ui->StopBitsBox->addItem("1");
    ui->StopBitsBox->addItem("2");
    ui->StopBitsBox->addItem("1.5");
}
/**
* @brief 将数据位添加到Box中
* Data5 5     Data6 6       Data7 7       Data8  8
*/
void CSerial::Play_DataBits(void)
{
    ui->DataBitsBox->addItem("5");
    ui->DataBitsBox->addItem("6");
    ui->DataBitsBox->addItem("7");
    ui->DataBitsBox->addItem("8");
}
/**
* @brief 将校验添加到Box中
* NoParity 0        EvenParity 2         OddParity   3
*/
void CSerial::Play_Parity(void)
{
    ui->ParityBox->addItem("No Parity");
    ui->ParityBox->addItem("Even");
    ui->ParityBox->addItem("Odd");
}

将文本加载完后, 需要根据文本内容对端口进行初始化, 不然每次使用都需要用户一个个配置(防止用户对未配置的端口进行操作)

/**
* @brief 根据Box中的内容初始化端口
*/
void CSerial::Port_Init(void)
{
    int index;
    OCFlag = 0;
    HexFlag = 0;
    TimerFlag = 0;
    currentPort.setPortName(ports[ui->comboBox->currentIndex()].portName());
    currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());
    currentPort.setStopBits(QSerialPort::StopBits(ui->StopBitsBox->currentIndex()+1));
    currentPort.setDataBits(QSerialPort::DataBits(ui->DataBitsBox->currentIndex()+5));
    index = ui->ParityBox->currentIndex();
    index = index == 0 ? index : (index+1);
    currentPort.setParity(QSerialPort::Parity(index));
    currentPort.setFlowControl(QSerialPort::NoFlowControl);
    qDebug() << currentPort.portName() << Qt::endl;
    qDebug() << currentPort.baudRate() << Qt::endl;
    qDebug() << currentPort.stopBits() << Qt::endl;
    qDebug() << currentPort.dataBits() << Qt::endl;
    qDebug() << currentPort.parity() << Qt::endl;
}

串口配置

初始化完界面后,实现串口配置的功能

串口配置,有选择串口号配置波特率配置停止位, 配置数据位配置校验位

选择串口号

// 选择端口
    connect(ui->comboBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << ui->comboBox->currentText() << Qt::endl;
        currentPort.setPortName(ports[index].portName());
        qDebug() << currentPort.portName();
    });
  • connect()在Qt中将信号与槽进行连接, QOverload<int>::of(&QComboBox::activated) , 这里的槽函数是个匿名函数
// 槽函数
[=](int index){
        qDebug() << index << ui->comboBox->currentText() << Qt::endl;
        currentPort.setPortName(ports[index].portName());
        qDebug() << currentPort.portName();
    }

连接好信号(Signal)与槽(Slots)后, 简单讲解下实现的机制

  • 在用户对ComboBox进行操作时或者ComboBox本身的属性发生改变时, 如点击或者ComboBox的现在的文本内容发生改变, ComboBox对象会产生一个信号(这里是activated, 鼠标点击内容后触发)
  • 这是文档中ComboBox对象能够产生的信号

在这里插入图片描述

  • 点进去后会有说明和例子

在这里插入图片描述

  • connect() 连接好信号与槽后, 会在收到信号后直接执行槽函数, 槽函数可以是普通函数或者匿名函数, 这里主要是调用了匿名函数,匿名函数内再调用currentPort.setPortName(ports[index].portName())将现在端口的名字设置为鼠标点中的内容

  • 学过单片机的,可以简单将信号与槽理解为中断信号中断服务函数

  • 除了一些C++的基本语法Qt的重点就是信号与槽

配置波特率

与上面类似,直接放代码

// 选择波特率
    connect(ui->BoudRateBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());
        qDebug() << currentPort.baudRate() << Qt::endl;
    });

配置停止位

 // 选择停止位
    connect(ui->StopBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        currentPort.setStopBits(QSerialPort::StopBits(index+1));
        qDebug() << currentPort.stopBits() << Qt::endl;
    });

配置数据位

// 选择数据位
    connect(ui->DataBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        currentPort.setDataBits(QSerialPort::DataBits(index+5));
        qDebug() << currentPort.dataBits() << Qt::endl;
    });

配置校验位

// 选择校验
    connect(ui->ParityBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        index = index == 0 ? index : (index+1);
        currentPort.setParity(QSerialPort::Parity(index));
        qDebug() << currentPort.parity() << Qt::endl;
     });

基础功能实现

打开和关闭串口

配置完串口后,就可以打开和关闭串口了

OCFlag==0时, 调用open打开串口, 并将按钮的Icon切换, 若打开串口失败则调用QMessageBox::warning弹出提示框;当OCFlag==1时,调用close()关闭串口, 并将按钮的Icon切换.

/**
* @brief 打开和关闭串口
*/
void CSerial::OpenAndClose_Port(void)
{
    if(OCFlag == 0)
    {
        if(currentPort.open(QIODevice::ReadWrite))
        {
            qDebug() << "串口已打开" << Qt::endl;
            ui->OpenSerial->setIcon(QIcon(":/Image/off.png"));
            ui->OpenSerial->setText("关闭串口");
            OCFlag = 1;
        }
        else
        {
            QMessageBox::warning(this, "Error", "Cannot Open Port:" + currentPort.errorString());
        }
    }
    else
    {
        currentPort.close();
        ui->OpenSerial->setIcon(QIcon(":/Image/On.png"));
        qDebug() << "串口已关闭" << Qt::endl;
        ui->OpenSerial->setText("打开串口");
        OCFlag = 0;
    }
}

槽函数写完,将其与一个按钮连接

// 打开关闭串口
    connect(ui->OpenSerial, &QPushButton::clicked, this, &CSerial::OpenAndClose_Port);
  • 第一个参数是信号发出的对象,第二个参数是需要连接的信号,第三个参数是调用槽函数的对象,第四个参数是槽函数

  • OpenSerial是一个QpushButton类的对象,俗称"按钮", clicked为鼠标点击信号, this表示对象为该对象(一个Cserial类的对象), 槽函数是OpenAndClose_Port(打开或者关闭串口)

  • 执行完上面这行代码后, OpenSerial(按钮)被按下后发出一个信号,被监测到后调用,检测器通知this,然后this再调用OpenAndClose_Port 执行槽函数

  • 而若用匿名函数,则不需要槽函数的发起对象,因为在一个匿名函数中,可以调用不同对象的不同的函数, 所以指定槽函数发起对象失去意义

发送数据

打开串口后,就能够发送数据了

OCFlag==1时(串口打开时),调用write()发送数据;若bytesWritten==-1,则数据发送失败,用QMessageBox打印错误信息, 并将发送的信息显示在接受区

OCFlag==0时(串口关闭时),调用QMessageBox提示用户打开串口

/**
* @brief 发送数据
*/
void CSerial::Send_Data(void)
{
    if(OCFlag)
    {
        QString SendString = ui->SendText->toPlainText();
        qDebug() << SendString << Qt::endl;
        QByteArray dataToSend = SendString.toLocal8Bit();
        // 发送数据
        qint64 bytesWritten =currentPort.write(dataToSend);
        if(bytesWritten == -1)
        {
            QMessageBox::warning(this, "Error", "数据发送失败!");
        }
        else
        {
            QString SendedData = QString::fromStdString(dataToSend.toStdString());
            ui->textEdit->append("Tx:"+SendedData);
        }
    }
    else
    {
        QMessageBox::warning(this, "Error", "请先打开串口!");
    }
}

槽函数写完,将其与一个按钮连接

// 发送数据
    connect(ui->SendData, &QPushButton::clicked, this, &CSerial::Send_Data);

接收数据

打开串口后,也可以接收到其他装置发送来的信息

接收到数据后,将数据显示到串口中, 若HexFlag==1,则以十六进制显示

/**
* @brief 接收数据
*/
void CSerial::Receive_Data(void)
{
    if(OCFlag)
    {
        QByteArray data = currentPort.readAll();
        QString text = QString::fromStdString(data.toStdString());
        qDebug() << text << Qt::endl;
        if(HexFlag == 0)
        {
            ui->textEdit->append("Rx:" + text);
        }
        else
        {
            ui->textEdit->append("Rx:" + data.toHex());
        }
    }
}

槽函数写完后,将其与信号连接, 该信号由串口产生; readRead在串口读取完数据后产生

// 接收数据
    connect(&currentPort, &QSerialPort::readyRead, this, &CSerial::Receive_Data);

保存接收区内容

收到数据后,可以将数据以.txt文件的形式保存到指定路径

通过QFileDialog::getSaveFileName()获取保存路径, 它的最后一个参数将保存文件类型限制为.txt

通过QFile对象打开文件,若打开失败则通过QMessageBox弹出错误信息窗口

通过textEdit对象的toPlainText()方法获取接收区数据并转换为纯文本

通过QFile对象write()方法将文本以utf-8的格式写入文件中

通过close()方法关闭文件,并弹出保存成功的提示窗口

/**
* @brief 保存接受区内容
*/
void CSerial::Save_RxText(void)
{
    // 获取文件保存路径
    QString fileName = QFileDialog::getSaveFileName(this, "Save", "/text", tr("Text (*.txt)"));
    qDebug() << fileName << Qt::endl;
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly | QFile::Text))
    {
        QMessageBox::warning(this, "Warning", "Cannot Save file: " + file.errorString());
        return;
    }
    setWindowTitle(fileName);
    QTextStream out(&file);
    QString text = ui->textEdit->toPlainText();
    file.write(text.toUtf8());
    file.close();
    QMessageBox::information(this, "提示", "保存成功!");
}

清除接收区和发送区内容

清除内容,其实没必要封装起来

/**
* @brief 清除发送区内容
*/
void CSerial::Clear_SendText(void)
{
    ui->SendText->clear();
}
/**
* @brief 清除接收区内容
*/
void CSerial::Clear_RxText(void)
{
    ui->textEdit->clear();
}
// 清除发送区文本内容
    connect(ui->ClearData, &QPushButton::clicked, this, &CSerial::Clear_SendText);
// 清除接收区文本内容
    connect(ui->Clear_Rx, &QPushButton::clicked, this, &CSerial::Clear_RxText);

改变十六进制标志

改变十六进制标志,封装起来思路清晰一点吧

/**
* @brief 改变十六进制标志
*/
void CSerial::Change_HexFlag(void)
{
    qDebug() << ui->checkBox->checkState() << Qt::endl;
    HexFlag = ui->checkBox->checkState();
    qDebug() << HexFlag << Qt::endl;
}
// 改变十六进制接收标志
    connect(ui->checkBox, &QCheckBox::stateChanged, this, &CSerial::Change_HexFlag);

定时发送

  • 定时发送相关的函数有三个

  • 当定时发送的按钮被触动后,会触发槽函数,在槽函数中先检测标志位,再设置定时器发送周期,最后根据定时发送标志位和串口关闭标志位判断是开启定时器还是关闭定时器

  • 定时器槽函数中调用Send_Data()发送数据

  • 下面简单介绍下QTimer定时器,主要就是用来定时,start(Time)开启定时器并设置周期,单位是毫秒,定时时间到发出timeout信号,用connect将信号与槽连接后,达到每过Time触发一次槽函数

/**
* @brief 改变定时发送标志
*/
void CSerial::Change_TimerFlag(void)
{
    qDebug() << ui->TimerCheck->checkState() << Qt::endl;
    TimerFlag = ui->TimerCheck->checkState();
    qDebug() << TimerFlag << Qt::endl;
}

/**
* @brief 设置定时器周期
*/
void CSerial::Set_TimerTime(void)
{
    bool Flag;
    int temp;
    temp = ui->TimerEdit->text().toInt(&Flag);
    if(Flag)
    {
        Timer_Time = temp;
        qDebug() << "T = " << Timer_Time << " ms" << Qt::endl;
    }
    else
    {
        QMessageBox::warning(this, "Error", "请输入合法的数字");
    }
}

/**
* @brief 定时发送
*/
void CSerial::Send_InTime(void)
{
    this->Change_TimerFlag();  // 改变标志位
    this->Set_TimerTime();   // 设置发送周期
    if(TimerFlag && OCFlag)
    {
        Timer->start(Timer_Time);  // 启动定时器
    }
    else
    {
        Timer->stop();   // 关闭定时器
        if(TimerFlag) QMessageBox::warning(this, "Error", "请先打开串口!");
    }
}
// 定时发送数据
    connect(ui->TimerCheck, &QCheckBox::stateChanged, this, &CSerial::Send_InTime);
    Timer = new QTimer(this);
    connect(Timer, &QTimer::timeout, [=](){
        this->Send_Data();
    });

整体程序

cserial.h

#ifndef CSERIAL_H
#define CSERIAL_H

#include <QMainWindow>
#include <QSerialPort>
#include <QList>
#include <QSerialPortInfo>
#include <QDebug>
#include <QComboBox>
#include <QMessageBox>
#include <QFileDialog>
#include <QFile>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class CSerial; }
QT_END_NAMESPACE

class CSerial : public QMainWindow
{
    Q_OBJECT

public:
    CSerial(QWidget *parent = nullptr);
    ~CSerial();
private slots:   // 定义的槽函数
    void Scan_Serial(void);
    void Play_BoudRate(void);
    void Play_StopBits(void);
    void Play_DataBits(void);
    void Play_Parity(void);
    void Port_Init(void);
    void OpenAndClose_Port(void);
    void Send_Data(void);
    void Receive_Data(void);
    void Clear_SendText(void);
    void Save_RxText(void);
    void Clear_RxText(void);
    void Change_HexFlag(void);
    void Change_TimerFlag(void);
    void Set_TimerTime(void);
    void Send_InTime(void);
private:
    int OCFlag;  // 打开关闭标志位
    int HexFlag;  // 十六进制显示标志位
    int TimerFlag;  // 定时发送标志位
    int Timer_Time;  // 定时发送周期
    QTimer * Timer;  // 定时器
    Ui::CSerial *ui;
    QSerialPort currentPort;  // 当前端口
    QList<QSerialPortInfo> ports;  // 所有可以端口信息
};
#endif // CSERIAL_H

cserial.cpp

#include "cserial.h"
#include "ui_cserial.h"

CSerial::CSerial(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::CSerial)
{
    ui->setupUi(this);
    ui->OpenSerial->setIcon(QIcon(":/Image/On.png"));
    ui->Save_Rx->setIcon(QIcon(":/Image/save.png"));
    ui->ClearData->setIcon(QIcon(":/Image/clear.png"));
    ui->Clear_Rx->setIcon(QIcon(":/Image/clear.png"));
    ui->SendData->setIcon(QIcon(":/Image/send.png"));
    this->setWindowIcon(QIcon(":/Image/serial.png"));
    this->Scan_Serial();
    this->Play_BoudRate();
    this->Play_StopBits();
    this->Play_DataBits();
    this->Play_Parity();
    this->Port_Init();
    // 选择端口
    connect(ui->comboBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << ui->comboBox->currentText() << Qt::endl;
        currentPort.setPortName(ports[index].portName());
        qDebug() << currentPort.portName();
    });
    // 选择波特率
    connect(ui->BoudRateBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());
        qDebug() << currentPort.baudRate() << Qt::endl;
    });
    // 选择停止位
    connect(ui->StopBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        currentPort.setStopBits(QSerialPort::StopBits(index+1));
        qDebug() << currentPort.stopBits() << Qt::endl;
    });
    // 选择数据位
    connect(ui->DataBitsBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        currentPort.setDataBits(QSerialPort::DataBits(index+5));
        qDebug() << currentPort.dataBits() << Qt::endl;
    });
    // 选择校验
    connect(ui->ParityBox, QOverload<int>::of(&QComboBox::activated), [=](int index){
        qDebug() << index << Qt::endl;
        index = index == 0 ? index : (index+1);
        currentPort.setParity(QSerialPort::Parity(index));
        qDebug() << currentPort.parity() << Qt::endl;
     });
    // 打开关闭串口
    connect(ui->OpenSerial, &QPushButton::clicked, this, &CSerial::OpenAndClose_Port);
    // 发送数据
    connect(ui->SendData, &QPushButton::clicked, this, &CSerial::Send_Data);
    // 接收数据
    connect(&currentPort, &QSerialPort::readyRead, this, &CSerial::Receive_Data);
    // 清除发送区文本内容
    connect(ui->ClearData, &QPushButton::clicked, this, &CSerial::Clear_SendText);
    // 保存接受区文本内容
    connect(ui->Save_Rx, &QPushButton::clicked, this, &CSerial::Save_RxText);
    // 清除接收区文本内容
    connect(ui->Clear_Rx, &QPushButton::clicked, this, &CSerial::Clear_RxText);
    // 改变十六进制接收标志
    connect(ui->checkBox, &QCheckBox::stateChanged, this, &CSerial::Change_HexFlag);
    // 定时发送数据
    connect(ui->TimerCheck, &QCheckBox::stateChanged, this, &CSerial::Send_InTime);
    Timer = new QTimer(this);
    connect(Timer, &QTimer::timeout, [=](){
        this->Send_Data();
    });
}

CSerial::~CSerial()
{
    currentPort.close();  // 关闭串口
    delete ui;
}

/**
* @brief 扫描所有可用的串口,并将其添加到Box中
*/
void CSerial::Scan_Serial(void)
{
    ports = QSerialPortInfo::availablePorts();
    foreach(QSerialPortInfo info, ports)
    {
        qDebug() << "串口名称" << info.portName();
        qDebug() << "设备ID:" << info.description();
        ui->comboBox->addItem(QString(info.portName() + info.description()));
    }
}

/**
* @brief 将支持的波特率添加到Box中
* 1200 2400 4800 9600 19200 38400 57600 115200
*/
void CSerial::Play_BoudRate(void)
{
    ui->BoudRateBox->addItem("1200");
    ui->BoudRateBox->addItem("2400");
    ui->BoudRateBox->addItem("4800");
    ui->BoudRateBox->addItem("9600");
    ui->BoudRateBox->addItem("19200");
    ui->BoudRateBox->addItem("38400");
    ui->BoudRateBox->addItem("57600");
    ui->BoudRateBox->addItem("115200");
}

/**
* @brief 将停止位添加到Box中
* OneStop 1      TowStop 2     OneAndHalfStop 3
*/
void CSerial::Play_StopBits(void)
{
    ui->StopBitsBox->addItem("1");
    ui->StopBitsBox->addItem("2");
    ui->StopBitsBox->addItem("1.5");
}

/**
* @brief 将数据位添加到Box中
* Data5 5     Data6 6       Data7 7       Data8  8
*/
void CSerial::Play_DataBits(void)
{
    ui->DataBitsBox->addItem("5");
    ui->DataBitsBox->addItem("6");
    ui->DataBitsBox->addItem("7");
    ui->DataBitsBox->addItem("8");
}

/**
* @brief 将校验添加到Box中
* NoParity 0        EvenParity 2         OddParity   3
*/
void CSerial::Play_Parity(void)
{
    ui->ParityBox->addItem("No Parity");
    ui->ParityBox->addItem("Even");
    ui->ParityBox->addItem("Odd");
}

/**
* @brief 根据Box中的内容初始化端口
*/
void CSerial::Port_Init(void)
{
    int index;
    OCFlag = 0;
    HexFlag = 0;
    TimerFlag = 0;
    currentPort.setPortName(ports[ui->comboBox->currentIndex()].portName());
    currentPort.setBaudRate(ui->BoudRateBox->currentText().toInt());
    currentPort.setStopBits(QSerialPort::StopBits(ui->StopBitsBox->currentIndex()+1));
    currentPort.setDataBits(QSerialPort::DataBits(ui->DataBitsBox->currentIndex()+5));
    index = ui->ParityBox->currentIndex();
    index = index == 0 ? index : (index+1);
    currentPort.setParity(QSerialPort::Parity(index));
    currentPort.setFlowControl(QSerialPort::NoFlowControl);
    qDebug() << currentPort.portName() << Qt::endl;
    qDebug() << currentPort.baudRate() << Qt::endl;
    qDebug() << currentPort.stopBits() << Qt::endl;
    qDebug() << currentPort.dataBits() << Qt::endl;
    qDebug() << currentPort.parity() << Qt::endl;
}

/**
* @brief 打开和关闭串口
*/
void CSerial::OpenAndClose_Port(void)
{
    if(OCFlag == 0)
    {
        if(currentPort.open(QIODevice::ReadWrite))
        {
            qDebug() << "串口已打开" << Qt::endl;
            ui->OpenSerial->setIcon(QIcon(":/Image/off.png"));
            ui->OpenSerial->setText("关闭串口");
            OCFlag = 1;
        }
        else
        {
            QMessageBox::warning(this, "Error", "Cannot Open Port:" + currentPort.errorString());
        }
    }
    else
    {
        currentPort.close();
        ui->OpenSerial->setIcon(QIcon(":/Image/On.png"));
        qDebug() << "串口已关闭" << Qt::endl;
        ui->OpenSerial->setText("打开串口");
        OCFlag = 0;
    }
}

/**
* @brief 发送数据
*/
void CSerial::Send_Data(void)
{
    if(OCFlag)
    {
        QString SendString = ui->SendText->toPlainText();
        qDebug() << SendString << Qt::endl;
        QByteArray dataToSend = SendString.toLocal8Bit();
        // 发送数据
        qint64 bytesWritten =currentPort.write(dataToSend);
        if(bytesWritten == -1)
        {
            QMessageBox::warning(this, "Error", "数据发送失败!");
        }
        else
        {
            QString SendedData = QString::fromStdString(dataToSend.toStdString());
            ui->textEdit->append("Tx:"+SendedData);
        }
    }
    else
    {
        QMessageBox::warning(this, "Error", "请先打开串口!");
    }
}

/**
* @brief 接收数据
*/
void CSerial::Receive_Data(void)
{
    if(OCFlag)
    {
        QByteArray data = currentPort.readAll();
        QString text = QString::fromStdString(data.toStdString());
        qDebug() << text << Qt::endl;
        if(HexFlag == 0)
        {
            ui->textEdit->append("Rx:" + text);
        }
        else
        {
            ui->textEdit->append("Rx:" + data.toHex());
        }
    }
}

/**
* @brief 清除发送区内容
*/
void CSerial::Clear_SendText(void)
{
    ui->SendText->clear();
}

/**
* @brief 保存接受区内容
*/
void CSerial::Save_RxText(void)
{
    // 获取文件保存路径
    QString fileName = QFileDialog::getSaveFileName(this, "Save", "/text", tr("Text (*.txt)"));
    qDebug() << fileName << Qt::endl;
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly | QFile::Text))
    {
        QMessageBox::warning(this, "Warning", "Cannot Save file: " + file.errorString());
        return;
    }
    setWindowTitle(fileName);
    QTextStream out(&file);
    QString text = ui->textEdit->toPlainText();
    file.write(text.toUtf8());
    file.close();
    QMessageBox::information(this, "提示", "保存成功!");
}

/**
* @brief 清除接收区内容
*/
void CSerial::Clear_RxText(void)
{
    ui->textEdit->clear();
}

/**
* @brief 改变十六进制标志
*/
void CSerial::Change_HexFlag(void)
{
    qDebug() << ui->checkBox->checkState() << Qt::endl;
    HexFlag = ui->checkBox->checkState();
    qDebug() << HexFlag << Qt::endl;
}

/**
* @brief 改变定时发送标志
*/
void CSerial::Change_TimerFlag(void)
{
    qDebug() << ui->TimerCheck->checkState() << Qt::endl;
    TimerFlag = ui->TimerCheck->checkState();
    qDebug() << TimerFlag << Qt::endl;
}

/**
* @brief 设置定时器周期
*/
void CSerial::Set_TimerTime(void)
{
    bool Flag;
    int temp;
    temp = ui->TimerEdit->text().toInt(&Flag);
    if(Flag)
    {
        Timer_Time = temp;
        qDebug() << "T = " << Timer_Time << " ms" << Qt::endl;
    }
    else
    {
        QMessageBox::warning(this, "Error", "请输入合法的数字");
    }
}

/**
* @brief 定时发送
*/
void CSerial::Send_InTime(void)
{
    this->Change_TimerFlag();
    this->Set_TimerTime();
    if(TimerFlag && OCFlag)
    {
        Timer->start(Timer_Time);
    }
    else
    {
        Timer->stop();
        if(TimerFlag) QMessageBox::warning(this, "Error", "请先打开串口!");
    }
}

main.cpp

#include "cserial.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    CSerial w;
    w.show();
    return a.exec();
}

源码提取

链接:https://pan.baidu.com/s/1a5b75bhMe2OeSB2egvImEw?pwd=nlxl
提取码:nlxl
–来自百度网盘超级会员V4的分享

参考资料

Qt Assistant(Qt助手)官方的文档还是好用的
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

招募 品牌设计师:最具创造力、破坏性、颠覆性

PIX Moving 寻 品牌设计师 重点要求「有破坏性」 设计需求 LOGO VI 市场方向 面向欧洲 案例参考 设计要求 &#xff5c; 这辆充满了创意和激情的 NEV 被命名为 Solo&#xff0c;它是 Z 世代用户的个人移动空间&#xff0c;强调个体的力量与价值。产品特征为去中心化创造、…

Zookeeper从零入门笔记

一、入门 1. 概述 2. 特点 3. 数据结构 4. 应用场景 统一命名服务&#xff1a;nginx也可以实现 统一配置管理&#xff1a; 统一集群管理&#xff1a; 服务器动态上下线&#xff1a; 软负载均衡&#xff1a; 二、本地 1.安装 2. 参数解读 三、集群操作 3.1.1 集群安装…

分享:大数据方向学生学徒参与条件

学生学徒制的实施旨在解决当前新技术企业招聘技能人才难和青年就业难的结构性矛盾&#xff0c;通过生态链链主企业携手院校共同解决毕业年度学生就业问题&#xff0c;按照学生个人意愿&#xff0c;建立以就业导向的学生学徒制关系&#xff0c;签订学徒培养协议确定学生就业岗位…

采购业务中的组织概述

目录 一、采购和库存管理中组织单位的概览二、企业的组织结构三、采购中组织结构3.1采购组织3.2采购组 一、采购和库存管理中组织单位的概览 1、 客户端&#xff1a;在SAP ERP系统中&#xff0c;客户端通过三位数字定义&#xff0c;并代表这独立的数据记录和独立的业务流程。客…

【带头学C++】----- 九、类和对象 ---- 9.1 类和对象的基本概念----(9.1.4---9.1.6)

目录 9.1.4 设计立方体类 ​编辑 9.1.5 成员函数在类的外部实现 9.1.6 类在其他源文件的实现步骤&#xff08;实现类在不同文件的实现&#xff0c;后续引出构造函数&#xff09; 注意:类定义在同文件testclass.h中&#xff0c;而testclass.cpp是用来实现&#xff08;声明&…

JAVA基础进阶(三)

一、权限修饰符的访问权限 需要特别注意的是: 被private修饰的成员变量以及成员方法只能在本类中进行调用&#xff0c;所以在其他类中创建本类对象,无法直接访问私有成员变量和成员方法,只能通过set、get方法间接访问。被public修饰的成员变量以及成员方法可以在任意地方被调用…

CHEM 14 not know

Goals of this lab: • Create and use a calibration curve for the absorbance/concentration relationship for crystal violet • Evaluate absorbance versus time measurements to determine the order of a reaction • Analyze graphs of data to determine best linea…

微信小程序自定义tabber凸起

一、实现效果 二、下载地址 下载地址 源码有错自己修改一下就行

Jmeter之压力测试总结!

一、基本概念 1.线程组N&#xff1a;代表一定数量的并发用户&#xff0c;所谓并发就是指同一时刻访问发送请求的用户。线程组就是模拟并发用户访问。 2.Ramp-Up Period(in seconds)&#xff1a;建立所有线程的周期&#xff0c;就是告诉jmeter要在多久没启动所有线程&#xff…

JMeter 常见易错问题

1、配置错误&#xff1a; 问题&#xff1a;线程组配置错误&#xff0c;例如设置了错误的线程数或循环次数。 解决方法&#xff1a;检查线程组的配置。确保线程数&#xff08;即并发用户数量&#xff09;设置正确&#xff0c;以及循环次数符合预期。如果要模拟不同类型的用户行…

CAS原理详解

文章目录 1. 问题引入2. CAS底层详解1. Java中CAS实现2. CAS源码分析3. CAS操作存在的缺陷4. ABA问题及其解决方案 1. 问题引入 见下面代码 public class Main {private volatile static int sum0;public static void main(String[] args) throws InterruptedException {for …

TZOJ 1376 母牛的故事(递推和递归)

答案1&#xff08;递推&#xff09;&#xff1a; #include<stdio.h> int main() {int n0,i0;int a[55] { 0,1,2,3,4 }; //数组下标就相当于过了几年&#xff0c;以第四年母牛生出的第一只小母牛成年为周期&#xff0c;初始化前四年的值while (scanf("%d", …

【Docker】Swarm的overlay网络

对于理解swarm的网络来讲&#xff0c;个人认为最重要的两个点&#xff1a; 第一是外部如何访问部署运行在swarm集群内的服务&#xff0c;可以称之为入方向流量&#xff0c;在swarm里我们通过ingress来解决。 第二是部署在swarm集群里的服务&#xff0c;如何对外进行访问&…

Linux环境搭建(Ubuntu22.04)+ 配置共享文件夹(Samba)

Linux开发环境准备 搭建Linux开发环境所需要的软件如下&#xff1a; VMware虚拟机&#xff1a;用于运行Linux操作系统的虚拟机软件之一&#xff0c;VMware下载安装在文章中不做说明&#xff0c;可自行百度谢谢Ubuntu光盘镜像&#xff1a;用于源代码编译&#xff0c;有闲置计算…

自己的测试技术烂, 不学几招怎么能快速提升自己!

很多小伙伴在成功入职后, 进入测试开发发展后, 都会进入一个瓶颈过渡期, 当然能够自己意识到这个问题说明还来得及&#xff01; 那么作为测试开发人员, 如何走出舒适区, 需要学习和掌握那些内容, 从而实现自己的最终目标呢?今天我们就来说一说, 在职场中如何不断的提升自己. …

【Android Studio学习】第一篇、制作一个拥有登录和注册功能的简易APP

目录 第一部分、前言 1、目标效果 2、准备知识 第二部分、详细步骤 1、新建Empty工程 ​2、添加资源文件 3、搭建注册界面 4、搭建登录界面 5、编写注册界面和登录界面的代码 6、设置APP初始界面 7、连接手机&#xff0c;编译工程 第三部分、总结 1、参考资料 2、…

Java+SSM+MySQL基于微信小程序的商城购物小程序(附源码 调试 文档)

基于微信小程序的商城购物小程序 一、引言二、国内外研究现状三、系统设计四、系统实现五、测试与评估六、结论七、界面展示八、源码获取 摘要&#xff1a; 本文介绍了一种基于微信小程序的商城购物小程序&#xff0c;该系统分为管理员和用户两种用户角色。管理员可以通过系统进…

LeetCode 7 整数反转

题目描述 整数反转 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示…

Mac电脑版程序创建工具 VMware InstallBuilder Enterprise mac最新

VMware InstallBuilder Enterprise 是一款功能强大、操作简单、跨平台支持的软件安装和部署工具&#xff0c;可以让开发者更加高效地创建和部署软件&#xff0c;并提供了丰富的功能和工具&#xff0c;适用于不同的用户需求和场景。 内置调试器 轻松排除应用程序安装过程中的故…

探索H5的神秘世界:测试点解析

Html5 app实际上是Web app的一种&#xff0c;在测试过程中可以延续Web App测试的部分方法&#xff0c;同时兼顾手机端的一些特性即可&#xff0c;下面帮大家总结下Html5 app 相关测试方法&#xff01; app内部H5测试点总结 1、业务逻辑 除基本功能测试外&#xff0c;需要关注的…