C++、QT项目——串口调试助手软件开发,技术文档解析,开发进度完成80%

 项目开发进度表:

20250628:增加串口读取打开功能
20250629:增加串口助手发送状态更新
20250630:1.新增按键关闭保护功能。2.新增定时自动发送功能
20250701:1.新增清空与保存接受的文本信息,并且存储为txt文本文件。2.利用qtimer库,使得该软件具备计时功能。
20250702:新增将文本utf8格式转化成16进制文本,再强转成qstring类型显示在文本控件中
20250703:1.增加了检查位16进制的文本码发送间隔 2.增加隐藏面板功能 3.每行代码写上注释
20250705:1.增加端口选择刷新检测功能。2.添加多文本串口发送功能。 

项目地址:

https://gitee.com/fan-wenshan/serial-debugging-assistant

软件界面:

串口调试助手

介绍

这是一个基于Qt框架开发的串口调试助手,专为嵌入式开发设计,提供直观的图形界面来进行串口通信调试。支持多种串口参数配置、数据发送接收、定时发送和历史记录等功能,帮助开发者高效调试串口设备。

功能特点

  • 自动检测并列出可用串口设备
  • 支持多种串口参数配置:
    • 波特率:1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200等
    • 数据位:5, 6, 7, 8位
    • 校验位:无校验、偶校验、奇校验、空格校验、标记校验
    • 停止位:1, 2位
    • 流控:无流控
  • 数据发送与接收功能
    • 支持字符串和HEX格式发送
    • 多文本预设发送区,可快速发送常用指令
    • 定时发送功能
  • 数据显示与管理
    • 接收数据实时显示
    • 发送/接收字节数统计
    • 历史记录保存与查看
    • 接收数据清空与保存
  • 用户友好界面
    • 分区布局:接收区、历史记录区、多文本发送区
    • 状态指示:发送成功/失败提示

软件架构

该项目基于Qt 5.12.12框架开发,采用C++语言编写,主要包含以下组件:

  • main.cpp:程序入口点,创建应用实例和主窗口
  • widget.h/widget.cpp:主窗口类实现,包含串口操作、UI交互等核心逻辑
  • widget.ui:使用Qt Designer设计的用户界面布局
  • res.qrc:资源文件,包含应用图标等资源
  • serial-debugging-assistant.pro:Qt项目文件

安装教程

环境要求

  • Qt 5.12.12或更高版本(推荐使用MinGW 32-bit编译器)
  • Windows操作系统

安装步骤

  1. 确保已安装Qt开发环境,建议使用Qt 5.12.12版本,并勾选MinGW 32-bit编译器组件
  2. 克隆或下载本项目到本地
  3. 打开Qt Creator,通过"文件"->"打开文件或项目"选择项目根目录下的serial-debugging-assistant.pro文件
  4. 选择构建套件(推荐:Desktop Qt 5.12.12 MinGW 32-bit)
  5. 点击"构建"->"构建项目"编译项目
  6. 编译完成后,点击"运行"按钮启动应用程序

使用方法

基本操作

  1. 启动应用程序,界面将显示所有可用串口
  2. 在串口配置区域选择合适的参数(波特率、数据位、校验位、停止位、流控)
  3. 点击"打开串口"按钮连接设备
  4. 在发送区输入要发送的数据
  5. 点击"发送"按钮发送数据
  6. 接收的数据将显示在接收区,并自动更新接收字节数统计

高级功能

  • 定时发送:勾选"定时发送"复选框,设置时间间隔,系统将自动循环发送当前输入框中的数据
  • 多文本发送:在右侧多文本区域预设常用指令,点击对应编号按钮快速发送
  • 历史记录:所有发送的指令会自动保存到历史记录区,方便查看和复用
  • 数据保存:点击"保存接收"按钮可将接收区数据保存到文件
  • 清空接收:点击"清空接收"按钮可清除接收区内容

项目结构

serial-debugging-assistant/
├── .gitee/              # Gitee相关配置文件
├── .gitignore           # Git忽略文件
├── LICENSE              # 许可证文件
├── README.md            # 中文说明文档
├── README.en.md         # 英文说明文档
├── Serial-Debug-Assistant.pro.user  # Qt项目用户配置
├── main.cpp             # 程序入口
├── mianicon.png         # 应用图标
├── res.qrc              # 资源文件
├── serial-debugging-assistant.pro  # Qt项目文件
├── widget.cpp           # 主窗口实现
├── widget.h             # 主窗口头文件
└── widget.ui            # 界面布局文件

贡献指南

  1. Fork本仓库
  2. 创建特性分支 (git checkout -b feature/xxx)
  3. 提交代码更改 (git commit -m 'Add some feature')
  4. 推送到分支 (git push origin feature/xxx)
  5. 创建Pull Request

许可证

本项目采用MIT许可证,详情请查看文件。

 软件界面(20250627):软件界面开发完毕 

软件界面(待开发中):

项目界面(待更新中): 

新增串口读取逻辑: 

#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include <QSerialPortInfo>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setLayout(ui->gridLayoutGlobal);

    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName();
        ui->comboBox_serialNum->addItem(serialInfo.portName());
    }

}

Widget::~Widget()
{
    delete ui;
}

从你提供的代码片段来看,你在 Widget 类的构造函数中尝试列出所有可用的串口,并将它们添加到一个名为 comboBox_serialNumQComboBox 控件中。这段代码看起来是正确的,但有几个方面需要注意以确保一切正常工作:

1. 确认 UI 文件中的控件名称

首先,确保在你的 .ui 文件(即 widget.ui)中确实有一个名为 comboBox_serialNumQComboBox 控件。如果这个控件不存在或名称不匹配,程序将在运行时抛出错误。

2. 设置布局

在你的代码中,有这一行:

this->setLayout(ui->gridLayoutGlobal);

确保 gridLayoutGlobal 已经在 .ui 文件中定义并且与主窗口部件关联。如果你已经在 .ui 文件中为顶层窗口设置了布局,则这一步可能是多余的,甚至可能导致问题。通常情况下,Qt Designer 自动生成的 .ui 文件会自动处理顶层布局,所以除非有特定需求,否则不需要手动设置。

3. 检查依赖项

确认你的 .pro 文件中已经包含了必要的模块:

QT += serialport

这是因为你使用了 QSerialPortInfo 类,它属于 serialport 模块。

4. 错误检查

如果你遇到任何编译或运行时错误,请仔细查看错误信息。例如,如果 comboBox_serialNum 未找到,你可能会看到类似以下的错误消息:

QObject::setParent: Cannot set parent, new parent is in a different thread

或者在运行时出现崩溃,可能是由于试图访问不存在的对象导致的空指针引用。

示例改进

这里是一个改进后的版本,增加了对 comboBox_serialNum 是否存在的检查,以避免潜在的运行时错误:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QSerialPortInfo>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 假设 gridLayoutGlobal 是你想要使用的主布局
    // this->setLayout(ui->gridLayoutGlobal); // 如果 .ui 文件已正确设置了主布局,此行可省略

    if (ui->comboBox_serialNum) { // 确保 comboBox_serialNum 存在
        QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
        for (const QSerialPortInfo &serialInfo : serialList) {
            qDebug() << "Available port:" << serialInfo.portName();
            ui->comboBox_serialNum->addItem(serialInfo.portName());
        }
    } else {
        qWarning() << "comboBox_serialNum not found!";
    }
}

Widget::~Widget()
{
    delete ui;
}

请根据实际情况调整上述代码。确保所有的 UI 元素都按预期命名,并且在 .pro 文件中正确配置了所有需要的 Qt 模块。如果还有其他具体的问题或错误信息,请提供详细情况以便进一步帮助。

#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include <QSerialPortInfo>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setLayout(ui->gridLayoutGlobal);
    serialPort = new QSerialPort(this);

    connect(serialPort, &QSerialPort::readyRead, this, &Widget::on_SerialData_readyToRead);

    ui->comboBox_boautrate->setCurrentIndex(6);
    ui->comboBox_databit->setCurrentIndex(3);

    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName();
        ui->comboBox_serialNum->addItem(serialInfo.portName());
    }

}

Widget::~Widget()
{
    delete ui;
}

/*
    enum Parity {
        NoParity = 0,
        EvenParity = 2,
        OddParity = 3,
        SpaceParity = 4,
        MarkParity = 5,
        UnknownParity = -1
    };*/

void Widget::on_btnCloseOrOpenSerial_clicked()
{
    //1.选择端口号
    serialPort->setPortName(ui->comboBox_serialNum->currentText());
    //2.配置波特率
    serialPort->setBaudRate(ui->comboBox_boautrate->currentText().toInt());
    //3.配置数据位
    serialPort->setDataBits(QSerialPort::DataBits(
                                ui->comboBox_databit->currentText().toUInt()));
    //4.配置校验位
    switch (ui->comboBox_checkbit->currentIndex()) {
    case 0 :
        serialPort->setParity(QSerialPort::NoParity);
        break;
    case 2 :
        serialPort->setParity(QSerialPort::EvenParity);
        break;
    case 3 :
        serialPort->setParity(QSerialPort::OddParity);
        break;
    case 4 :
        serialPort->setParity(QSerialPort::SpaceParity);
        break;
    case 5 :
        serialPort->setParity(QSerialPort::MarkParity);
        break;
    default:
        serialPort->setParity(QSerialPort::UnknownParity);
        break;
    }
    //5.配置停止位
    serialPort->setStopBits(QSerialPort::StopBits(
                                ui->comboBox_stopbit->currentText().toUInt()));
    //6.流控
    if(ui->comboBox_fileCon->currentText() == "None")
        serialPort->setFlowControl(QSerialPort::NoFlowControl);
    //7.打开串口
    if(serialPort->open(QIODevice::ReadWrite))
        qDebug() << "seriarl open successful";
}


void Widget::on_btnSendContext_clicked()
{
    const char* sendData = ui->lineEditSendContext->text().toStdString().c_str();
    serialPort->write(sendData);
    qDebug() << "send OK" << sendData;
    ui->textEditRecord->append(sendData);
}

void Widget::on_SerialData_readyToRead()
{
    QString revMassage = serialPort->readAll();
    qDebug() << "getMassage:" << revMassage;
    ui->textEditRev->append(revMassage);
}

//点击btnSendContext为什么textEditRev没有接收到qstring的字符串?

你遇到的问题是:

点击 btnSendContext 发送数据后,textEditRev(接收区)没有显示串口返回的内容,而是希望看到发送后设备回传的数据。

但目前 textEditRev 没有更新,也没有收到任何内容。


✅ 问题分析

从你的代码来看:

void Widget::on_btnSendContext_clicked()
{
    const char* sendData = ui->lineEditSendContext->text().toStdString().c_str();
    serialPort->write(sendData);
    qDebug() << "send OK" << sendData;
    ui->textEditRecord->append(sendData);
}

你使用了 QSerialPort::write() 发送数据,但 并没有触发 readyRead 信号,所以 on_SerialData_readyToRead() 没有被调用,textEditRev 当然不会有任何更新。


❓ 为什么没有触发 readyRead?

因为 readyRead 是当串口接收到数据时才会触发的信号。而你只是发了数据,并不代表设备立即响应并返回了数据。

所以你需要确认以下几点:

  1. 目标设备是否真的在收到数据后返回了响应?
  2. 串口是否真的打开成功?
  3. 波特率、校验位等设置是否与设备一致?

🔍 分步排查建议

✅ 1. 检查串口是否打开成功

你当前只打印了一句调试信息:

qDebug() << "seriarl open successful";

建议加上失败处理:

if (serialPort->open(QIODevice::ReadWrite)) {
    qDebug() << "Serial opened successfully.";
} else {
    qDebug() << "Failed to open serial port:" << serialPort->errorString();
}

这样可以知道是不是根本没打开串口。


✅ 2. 检查串口配置是否正确

确保你配置的参数和设备完全一致,包括:

  • 波特率
  • 数据位
  • 校验位
  • 停止位
  • 流控制

例如:

serialPort->setBaudRate(QSerialPort::Baud9600); // 或使用下拉框选择的值

如果你不确定设备的配置,可以用串口调试助手测试一下通信。


✅ 3. 手动测试串口是否能收发

你可以先写一个简单的测试函数,在点击按钮时发送固定字符串,比如 "Hello\n",然后手动查看是否有返回数据。

void Widget::on_btnTestSend_clicked()
{
    serialPort->write("Hello\n");
}

并在 on_SerialData_readyToRead() 中打印收到的内容:

void Widget::on_SerialData_readyToRead()
{
    QByteArray data = serialPort->readAll();
    QString revMessage = QString::fromUtf8(data);
    qDebug() << "Received data:" << revMessage;
    ui->textEditRev->append(revMessage);
}

如果仍然不触发 readyRead,说明串口根本没有收到数据。


✅ 4. 使用虚拟串口工具测试通信

如果硬件设备不可控,可以使用虚拟串口工具(如 VSPD、USB转TTL模块连接两个串口调试助手),模拟串口通信。


🧪 示例改进后的完整代码片段

void Widget::on_btnSendContext_clicked()
{
    QString textToSend = ui->lineEditSendContext->text();
    if (!textToSend.isEmpty()) {
        QByteArray sendData = textToSend.toUtf8(); // 推荐使用 UTF-8 编码
        qint64 bytesWritten = serialPort->write(sendData);

        if (bytesWritten == -1) {
            qDebug() << "Failed to write to serial port.";
        } else {
            qDebug() << "Sent" << bytesWritten << "bytes:" << textToSend;
            ui->textEditRecord->append("[发送] " + textToSend);
        }
    }
}

void Widget::on_SerialData_readyToRead()
{
    QByteArray data = serialPort->readAll();
    if (!data.isEmpty()) {
        QString revMessage = QString::fromUtf8(data);
        qDebug() << "Received data:" << revMessage;
        ui->textEditRev->append("[接收] " + revMessage);
    }
}

✅ 总结:你为什么看不到接收数据?

原因是否可能
设备未返回数据✅ 很有可能
串口未打开✅ 需要检查
参数配置错误✅ 波特率/校验位/停止位不匹配
没有实际物理连接✅ 需要测试环境
没有真正触发 readyRead✅ 因为没有收到数据

✅ 下一步建议

  1. 加日志输出:每一步都打印调试信息;
  2. 确认串口打开状态
  3. 使用串口调试助手验证设备通信逻辑
  4. 尝试发送固定命令测试设备是否响应
  5. 确保使用正确的编码格式(如 UTF-8)解析数据
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QSerialPortInfo>

// 构造函数:初始化Widget并设置串口参数
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    // 从.ui文件加载界面组件
    ui->setupUi(this);
    // 设置主布局为gridLayoutGlobal
    this->setLayout(ui->gridLayoutGlobal);
    // 初始化发送和接收计数器
    writeCntTotal = 0;
    readCntTotal = 0;
    // 创建一个新的QSerialPort对象
    serialPort = new QSerialPort(this);

    // 连接readyRead信号到槽函数on_SerialData_readyToRead
    connect(serialPort, &QSerialPort::readyRead, this, &Widget::on_SerialData_readyToRead);

    // 设置波特率下拉框默认选中项(第7个选项)
    ui->comboBox_boautrate->setCurrentIndex(6);
    // 设置数据位下拉框默认选中项(第4个选项)
    ui->comboBox_databit->setCurrentIndex(3);

    // 获取所有可用串口信息,并添加到comboBox_serialNum下拉框
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName(); // 打印串口名称
        ui->comboBox_serialNum->addItem(serialInfo.portName()); // 添加到下拉框
    }
}

// 析构函数:释放资源
Widget::~Widget()
{
    delete ui; // 删除UI对象
}

/*
enum Parity {
    NoParity = 0,
    EvenParity = 2,
    OddParity = 3,
    SpaceParity = 4,
    MarkParity = 5,
    UnknownParity = -1
};*/

// 按钮点击事件处理函数:打开或关闭串口
void Widget::on_btnCloseOrOpenSerial_clicked()
{
    // 选择端口号
    serialPort->setPortName(ui->comboBox_serialNum->currentText());
    // 配置波特率
    serialPort->setBaudRate(ui->comboBox_boautrate->currentText().toInt());
    // 配置数据位
    serialPort->setDataBits(QSerialPort::DataBits(
                                ui->comboBox_databit->currentText().toUInt()));
    // 配置校验位
    switch (ui->comboBox_checkbit->currentIndex()) {
    case 0 :
        serialPort->setParity(QSerialPort::NoParity);
        break;
    case 2 :
        serialPort->setParity(QSerialPort::EvenParity);
        break;
    case 3 :
        serialPort->setParity(QSerialPort::OddParity);
        break;
    case 4 :
        serialPort->setParity(QSerialPort::SpaceParity);
        break;
    case 5 :
        serialPort->setParity(QSerialPort::MarkParity);
        break;
    default:
        serialPort->setParity(QSerialPort::UnknownParity);
        break;
    }
    // 配置停止位
    serialPort->setStopBits(QSerialPort::StopBits(
                                ui->comboBox_stopbit->currentText().toUInt()));
    // 流控配置
    if(ui->comboBox_fileCon->currentText() == "None")
        serialPort->setFlowControl(QSerialPort::NoFlowControl);
    // 打开串口
    if(serialPort->open(QIODevice::ReadWrite))
        qDebug() << "seriarl open successful";
}

// 发送按钮点击事件处理函数:发送文本框中的内容
void Widget::on_btnSendContext_clicked()
{
    int writeCnt = 0;
    // 将文本框中的内容转换为C风格字符串
    const char* sendData = ui->lineEditSendContext->text().toStdString().c_str();
    // 写入数据到串口
    writeCnt = serialPort->write(sendData);
    // 判断写入是否成功
    if(writeCnt == -1){
        ui->labelSendStatus->setText("SendError!"); // 设置发送状态标签
    }else{
        writeCntTotal += writeCnt; // 更新总发送字节数
        qDebug() << "send OK" << sendData; // 打印发送成功的消息
        ui->textEditRecord->append(sendData); // 在记录框中追加发送的内容
        ui->labelSendStatus->setText("SendOK!"); // 设置发送状态标签
        ui->labelSendcnt->setNum(writeCntTotal); // 更新发送字节总数显示
    }
}

// 当串口有可读数据时触发此槽函数
void Widget::on_SerialData_readyToRead()
{
    // 读取所有可读数据
    QString revMassage = serialPort->readAll();
    // 如果收到的数据非空,则进行处理
    if(revMassage != NULL){
        qDebug() << "getMassage:" << revMassage; // 打印收到的消息
        ui->textEditRev->append(revMassage); // 在接收框中追加收到的内容
        readCntTotal += revMassage.size(); // 更新总接收字节数
        ui->labelRevcnt->setNum(readCntTotal); // 更新接收字节总数显示
    }
}

20250630:1,新增按键关闭保护功能。2,新增定时自动发送功能 

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QSerialPortInfo>

// 构造函数:初始化Widget并设置串口参数
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    // 从.ui文件加载界面组件
    ui->setupUi(this);
    // 设置主布局为gridLayoutGlobal
    this->setLayout(ui->gridLayoutGlobal);

    // 初始化发送和接收计数器
    writeCntTotal = 0;
    readCntTotal = 0;

    // 初始串口状态为关闭
    serialStatus = false;

    // 默认禁用发送按钮
    ui->btnSendContext->setEnabled(false);

    // 创建一个新的QSerialPort对象
    serialPort = new QSerialPort(this);

    // 连接readyRead信号到槽函数on_SerialData_readyToRead
    connect(serialPort, &QSerialPort::readyRead, this, &Widget::on_SerialData_readyToRead);

    // 设置波特率下拉框默认选中项(第7个选项)
    ui->comboBox_boautrate->setCurrentIndex(6);

    // 设置数据位下拉框默认选中项(第4个选项)
    ui->comboBox_databit->setCurrentIndex(3);

    // 获取所有可用串口信息,并添加到comboBox_serialNum下拉框
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName(); // 打印串口名称
        ui->comboBox_serialNum->addItem(serialInfo.portName()); // 添加到下拉框
    }
}

// 析构函数:释放资源
Widget::~Widget()
{
    delete ui; // 删除UI对象
}

/*
enum Parity {
    NoParity = 0,
    EvenParity = 2,
    OddParity = 3,
    SpaceParity = 4,
    MarkParity = 5,
    UnknownParity = -1
};
*/

// 按钮点击事件处理函数:打开或关闭串口
void Widget::on_btnCloseOrOpenSerial_clicked()
{
    // 此函数被保留但未使用,可能是旧版本逻辑,当前被重载函数替代
}

// 发送按钮点击事件处理函数:发送文本框中的内容
void Widget::on_btnSendContext_clicked()
{
    int writeCnt = 0;

    // 将文本框中的内容转换为C风格字符串
    const char* sendData = ui->lineEditSendContext->text().toStdString().c_str();

    // 写入数据到串口
    writeCnt = serialPort->write(sendData);

    // 判断写入是否成功
    if(writeCnt == -1){
        ui->labelSendStatus->setText("SendError!"); // 设置发送状态标签
    }else{
        writeCntTotal += writeCnt; // 更新总发送字节数
        qDebug() << "send OK" << sendData; // 打印发送成功的消息
        ui->labelSendStatus->setText("SendOK!"); // 设置发送状态标签
        ui->labelSendcnt->setNum(writeCntTotal); // 更新发送字节总数显示

        // 如果本次发送的内容与上次不同,则追加记录
        if(strcmp(sendData, sendBak.toStdString().c_str()) != 0){
            ui->textEditRecord->append(sendData);  // 在记录框中追加发送的内容
            sendBak = QString(sendData);
        }
    }
}

// 当串口有可读数据时触发此槽函数
void Widget::on_SerialData_readyToRead()
{
    // 读取所有可读数据
    QString revMassage = serialPort->readAll();

    // 如果收到的数据非空,则进行处理
    if(revMassage != NULL){
        qDebug() << "getMassage:" << revMassage; // 打印收到的消息
        ui->textEditRev->append(revMassage); // 在接收框中追加收到的内容
        readCntTotal += revMassage.size(); // 更新总接收字节数
        ui->labelRevcnt->setNum(readCntTotal); // 更新接收字节总数显示
    }
}

// 重载的on_btnCloseOrOpenSerial_clicked函数,带bool参数表示按钮状态
void Widget::on_btnCloseOrOpenSerial_clicked(bool checked)
{
    qDebug() << "none checked" <<checked;

    if(checked){
        // 1.选择端口号
        serialPort->setPortName(ui->comboBox_serialNum->currentText());

        // 2.配置波特率
        serialPort->setBaudRate(ui->comboBox_boautrate->currentText().toInt());

        // 3.配置数据位
        serialPort->setDataBits(QSerialPort::DataBits(
                                    ui->comboBox_databit->currentText().toUInt()));

        // 4.配置校验位
        switch (ui->comboBox_checkbit->currentIndex()) {
        case 0 :
            serialPort->setParity(QSerialPort::NoParity);
            break;
        case 2 :
            serialPort->setParity(QSerialPort::EvenParity);
            break;
        case 3 :
            serialPort->setParity(QSerialPort::OddParity);
            break;
        case 4 :
            serialPort->setParity(QSerialPort::SpaceParity);
            break;
        case 5 :
            serialPort->setParity(QSerialPort::MarkParity);
            break;
        default:
            serialPort->setParity(QSerialPort::UnknownParity);
            break;
        }

        // 5.配置停止位
        serialPort->setStopBits(QSerialPort::StopBits(
                                    ui->comboBox_stopbit->currentText().toUInt()));

        // 6.流控配置
        if(ui->comboBox_fileCon->currentText() == "None")
            serialPort->setFlowControl(QSerialPort::NoFlowControl);

        // 7.打开串口
        if(serialPort->open(QIODevice::ReadWrite)){
            qDebug() << "seriarl open successful";

            // 禁用相关配置项防止更改
            ui->comboBox_databit->setEnabled(false);
            ui->comboBox_boautrate->setEnabled(false);
            ui->comboBox_checkbit->setEnabled(false);
            ui->comboBox_fileCon->setEnabled(false);
            ui->comboBox_serialNum->setEnabled(false);
            ui->comboBox_stopbit->setEnabled(false);

            // 改变按钮文字为“关闭串口”
            ui->btnCloseOrOpenSerial->setText("关闭串口");

            // 启用发送按钮
            ui->btnSendContext->setEnabled(true);
        }else{
            // 弹出错误提示框
            QMessageBox msgBox;
            msgBox.setWindowTitle("打开串口错误");
            msgBox.setText("打开失败!串口可能被占用或者已经被拔出!");
            msgBox.exec();
        }
    }else{
        // 关闭串口
        serialPort->close();

        // 改变按钮文字为“打开串口”
        ui->btnCloseOrOpenSerial->setText("打开串口");

        // 启用所有配置项
        ui->comboBox_databit->setEnabled(true);
        ui->comboBox_boautrate->setEnabled(true);
        ui->comboBox_checkbit->setEnabled(true);
        ui->comboBox_fileCon->setEnabled(true);
        ui->comboBox_serialNum->setEnabled(true);
        ui->comboBox_stopbit->setEnabled(true);

        // 禁用发送按钮
        ui->btnSendContext->setEnabled(false);
    }
}

// 复选框点击事件处理函数:定时发送功能
void Widget::on_checkBSendInTime_clicked(bool checked)
{
    // 当前为空实现,后续可用于实现定时发送功能
}
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QSerialPortInfo>
#include <QFileDialog>

// 构造函数:初始化Widget并设置串口参数
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    // 从.ui文件加载界面组件
    ui->setupUi(this);
    // 设置主布局为gridLayoutGlobal
    this->setLayout(ui->gridLayoutGlobal);

    // 初始化发送和接收计数器
    writeCntTotal = 0;
    readCntTotal = 0;

    // 初始串口状态为关闭
    serialStatus = false;

    // 默认禁用发送按钮
    ui->btnSendContext->setEnabled(false);

    // 默认禁用定时发送复选框
    ui->checkBSendInTime->setEnabled(false);

    // 创建一个新的QSerialPort对象
    serialPort = new QSerialPort(this);

    // 创建定时器用于定时发送功能
    timer = new QTimer(this);

    // 连接readyRead信号到槽函数on_SerialData_readyToRead
    connect(serialPort, &QSerialPort::readyRead, this, &Widget::on_SerialData_readyToRead);

    // 连接定时器超时信号到发送按钮点击处理函数
    connect(timer, &QTimer::timeout, [=](){
        on_btnSendContext_clicked();
    });

    // 设置波特率下拉框默认选中项(第7个选项)
    ui->comboBox_boautrate->setCurrentIndex(6);

    // 设置数据位下拉框默认选中项(第4个选项)
    ui->comboBox_databit->setCurrentIndex(3);

    // 获取所有可用串口信息,并添加到comboBox_serialNum下拉框
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName(); // 打印串口名称
        ui->comboBox_serialNum->addItem(serialInfo.portName()); // 添加到下拉框
    }
}

// 析构函数:释放资源
Widget::~Widget()
{
    delete ui; // 删除UI对象
}

/*
enum Parity {
    NoParity = 0,
    EvenParity = 2,
    OddParity = 3,
    SpaceParity = 4,
    MarkParity = 5,
    UnknownParity = -1
};
*/

// 按钮点击事件处理函数:打开或关闭串口
void Widget::on_btnCloseOrOpenSerial_clicked()
{
    // 此函数被保留但未使用,可能是旧版本逻辑,当前被重载函数替代
}

// 发送按钮点击事件处理函数:发送文本框中的内容
void Widget::on_btnSendContext_clicked()
{
    int writeCnt = 0;

    // 将文本框中的内容转换为C风格字符串,使用本地编码(如GBK/UTF-8)
    const char* sendData = ui->lineEditSendContext->text().toLocal8Bit().constData();

    // 写入数据到串口
    writeCnt = serialPort->write(sendData);

    // 判断写入是否成功
    if(writeCnt == -1){
        ui->labelSendStatus->setText("SendError!"); // 设置发送状态标签
    }else{
        writeCntTotal += writeCnt; // 更新总发送字节数
        qDebug() << "send OK" << sendData; // 打印发送成功的消息
        ui->labelSendStatus->setText("SendOK!"); // 设置发送状态标签
        // 显示更友好的发送统计格式
        ui->labelSendcnt->setText("Sent:" + QString::number(writeCntTotal));

        // 如果本次发送的内容与上次不同,则追加记录
        if(strcmp(sendData, sendBak.toStdString().c_str()) != 0){
            ui->textEditRecord->append(sendData);  // 在记录框中追加发送的内容
            sendBak = QString::fromUtf8(sendData); // 保存上一次发送的内容
        }
    }
}

// 当串口有可读数据时触发此槽函数
void Widget::on_SerialData_readyToRead()
{
    // 读取所有可读数据
    QString revMassage = serialPort->readAll();

    // 如果收到的数据非空,则进行处理
    if(revMassage != NULL){
        qDebug() << "getMassage:" << revMassage; // 打印收到的消息
        ui->textEditRev->append(revMassage); // 在接收框中追加收到的内容
        readCntTotal += revMassage.size(); // 更新总接收字节数
        // 显示更友好的接收统计格式
        ui->labelRevcnt->setText("Received:" + QString::number(readCntTotal));
    }
}

// 重载的on_btnCloseOrOpenSerial_clicked函数,带bool参数表示按钮状态
void Widget::on_btnCloseOrOpenSerial_clicked(bool checked)
{
    qDebug() << "none checked" <<checked;

    if(checked){
        // 1.选择端口号
        serialPort->setPortName(ui->comboBox_serialNum->currentText());

        // 2.配置波特率
        serialPort->setBaudRate(ui->comboBox_boautrate->currentText().toInt());

        // 3.配置数据位
        serialPort->setDataBits(QSerialPort::DataBits(
                                    ui->comboBox_databit->currentText().toUInt()));

        // 4.配置校验位
        switch (ui->comboBox_checkbit->currentIndex()) {
        case 0 :
            serialPort->setParity(QSerialPort::NoParity);
            break;
        case 2 :
            serialPort->setParity(QSerialPort::EvenParity);
            break;
        case 3 :
            serialPort->setParity(QSerialPort::OddParity);
            break;
        case 4 :
            serialPort->setParity(QSerialPort::SpaceParity);
            break;
        case 5 :
            serialPort->setParity(QSerialPort::MarkParity);
            break;
        default:
            serialPort->setParity(QSerialPort::UnknownParity);
            break;
        }

        // 5.配置停止位
        serialPort->setStopBits(QSerialPort::StopBits(
                                    ui->comboBox_stopbit->currentText().toUInt()));

        // 6.流控配置
        if(ui->comboBox_fileCon->currentText() == "None")
            serialPort->setFlowControl(QSerialPort::NoFlowControl);

        // 7.打开串口
        if(serialPort->open(QIODevice::ReadWrite)){
            qDebug() << "seriarl open successful";

            // 禁用相关配置项防止更改
            ui->comboBox_databit->setEnabled(false);
            ui->comboBox_boautrate->setEnabled(false);
            ui->comboBox_checkbit->setEnabled(false);
            ui->comboBox_fileCon->setEnabled(false);
            ui->comboBox_serialNum->setEnabled(false);
            ui->comboBox_stopbit->setEnabled(false);

            // 改变按钮文字为“关闭串口”
            ui->btnCloseOrOpenSerial->setText("关闭串口");

            // 启用发送按钮
            ui->btnSendContext->setEnabled(true);

            // 启用定时发送复选框
            ui->checkBSendInTime->setEnabled(true);
        }else{
            // 弹出错误提示框
            QMessageBox msgBox;
            msgBox.setWindowTitle("打开串口错误");
            msgBox.setText("打开失败!串口可能被占用或者已经被拔出!");
            msgBox.exec();
        }
    }else{
        // 关闭串口
        serialPort->close();

        // 改变按钮文字为“打开串口”
        ui->btnCloseOrOpenSerial->setText("打开串口");

        // 启用所有配置项
        ui->comboBox_databit->setEnabled(true);
        ui->comboBox_boautrate->setEnabled(true);
        ui->comboBox_checkbit->setEnabled(true);
        ui->comboBox_fileCon->setEnabled(true);
        ui->comboBox_serialNum->setEnabled(true);
        ui->comboBox_stopbit->setEnabled(true);

        // 禁用发送按钮
        ui->btnSendContext->setEnabled(false);
        // 取消勾选定时发送复选框
        ui->checkBSendInTime->setEnabled(false);
        ui->checkBSendInTime->setCheckState(Qt::Unchecked);
        // 停止定时器
        timer->stop();
        // 启用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(true);
        ui->lineEditSendContext->setEnabled(true);
    }
}

// 复选框点击事件处理函数:定时发送功能
void Widget::on_checkBSendInTime_clicked(bool checked)
{
    // 当前为空实现,后续可用于实现定时发送功能
    //    qDebug() << "checkedBsendIntiome:" << checked;
    if(checked){
        // 禁用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(false);
        ui->lineEditSendContext->setEnabled(false);
        // 启动定时器,时间间隔由用户输入决定
        timer->start(ui->lineEditTimeeach->text().toInt());
        //        on_btnSendContext_clicked();
    }else{
        // 停止定时器
        timer->stop();
        // 启用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(true);
        ui->lineEditSendContext->setEnabled(true);
    }
}

// 清除接收区域按钮点击事件处理函数
void Widget::on_btnrevClear_clicked()
{
    // 清空接收区文本
    ui->textEditRev->setText("");
}

// 保存接收数据按钮点击事件处理函数
void Widget::on_btnrevSave_clicked()
{
    // 弹出保存文件对话框,默认路径为 E:/QT/serialData.txt,只支持txt格式
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    "E:/QT/serialData.txt",
                                                    tr("Text (*.txt)"));
    if(fileName != NULL){
        QFile file(fileName);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            return;

        QTextStream out(&file);
        // 将接收区文本写入文件
        out << ui->textEditRev->toPlainText();
        file.close();
    }
}

void Widget::on_checkBHexDisplay_clicked(bool checked)
{
    if(checked){
        // 当复选框被选中时

        // 1. 从 textEditRev 控件中读取当前显示的纯文本内容
        QString tmp = ui->textEditRev->toPlainText();

        // 2. 将读取到的文本转换为 UTF-8 编码的字节数组,然后将该字节数组转换为十六进制表示形式
        QByteArray qtemp = tmp.toUtf8();     // 将 QString 转换为 QByteArray(UTF-8 格式)
        qtemp = qtemp.toHex();               // 将字节数组转换为十六进制字符串(如 "48656C6C6F")

        // 3. 将转换后的十六进制数据重新设置回 textEditRev 控件中(以十六进制形式显示)
        // 注意:qtemp 是 QByteArray 类型,需要转回 QString 显示
        ui->textEditRev->setText(QString::fromUtf8(qtemp));
    }else{
        // 当复选框未被选中时

        // 1. 从 textEditRev 控件中读取当前显示的内容(假设是十六进制格式)
        QString tmpHexString = ui->textEditRev->toPlainText();

        // 2. 将读取到的十六进制字符串转换回字节数组
        // 首先将十六进制字符串转换为 UTF-8 编码的字节数组
        QByteArray tmpHexQBytearray = tmpHexString.toUtf8();
        // 然后使用 fromHex 方法将十六进制编码的字节数组转换回原始的字节数组
        QByteArray tmpQbyteString = QByteArray::fromHex(tmpHexQBytearray);

        // 3. 将转换回的原始文本数据重新设置回 textEditRev 控件中(以文本形式显示)
        // 注意:tmpQbyteString 是 QByteArray 类型,需要转回 QString 显示
        ui->textEditRev->setText(QString::fromUtf8(tmpQbyteString));
    }
}

 注释解释

  1. 当复选框被选中时

    • 读取内容:首先从 textEditRev 控件中获取当前显示的所有文本。
    • 转换为十六进制:将获取到的文本转换为 UTF-8 编码的字节数组,然后进一步将其转换为十六进制字符串表示。
    • 更新显示:最后,将转换后的十六进制字符串重新设置为 textEditRev 的文本。
  2. 当复选框未被选中时

    • 读取内容:从 textEditRev 控件中获取当前显示的内容(假设这些内容是以十六进制格式存储的)。
    • 反转十六进制:将获取到的十六进制字符串转换回原始的字节数组。这一步首先需要将十六进制字符串转换为 UTF-8 编码的字节数组,然后再通过 fromHex 方法将其转换回原始的字节数组。
    • 更新显示:最终,将转换回的原始文本数据重新设置为 textEditRev 的文本。

这段代码实现了在勾选或取消勾选“以十六进制显示”选项时,在 textEditRev 控件中动态地切换文本和其对应的十六进制表示的功能。

以下是你提供的 on_checkBHexDisplay_clicked 函数的完整注释版代码,保留原始逻辑和格式,仅添加详细中文注释:

void Widget::on_checkBHexDisplay_clicked(bool checked)
{
    if(checked){
        // 当复选框被选中时(启用十六进制显示)

        // 1. 从 textEditRev 控件中读取当前显示的纯文本内容
        QString tmp = ui->textEditRev->toPlainText();

        // 2. 将读取到的文本转换为 UTF-8 编码的字节数组,
        // 然后将该字节数组转换为十六进制表示形式(小写)
        QByteArray qtemp = tmp.toUtf8();     // 将 QString 转换为 QByteArray(UTF-8 格式)
        qtemp = qtemp.toHex();               // 将字节数组转换为十六进制字符串(如 "48656c6c6f")

        QString lastshow;
        tmp = QString::fromUtf8(qtemp);      // 将 QByteArray 类型的十六进制数据转回 QString

        // 3. 将转换后的十六进制数据每两个字符一组进行分割,并在每组之间添加空格
        for (int i = 0; i < tmp.size(); i += 2) {
            lastshow += tmp.mid(i, 2) + " ";
        }

        // 最后将带有空格分隔的十六进制字符串以大写形式设置回 textEditRev 控件中
        ui->textEditRev->setText(lastshow.toUpper());
    }else{
        // 当复选框未被选中时(关闭十六进制显示)

        // 1. 从 textEditRev 控件中读取当前显示的内容(假设是十六进制格式字符串)
        QString tmpHexString = ui->textEditRev->toPlainText();

        // 2. 将读取到的十六进制字符串转换为 UTF-8 编码的字节数组
        QByteArray tmpHexQBytearray = tmpHexString.toUtf8();

        // 使用 fromHex 方法将十六进制编码的字节数组转换回原始的二进制字节数组
        QByteArray tmpQbyteString = QByteArray::fromHex(tmpHexQBytearray);

        // 3. 将转换回的原始二进制数据转换为 QString 并重新设置回 textEditRev 控件中
        // 以便以普通文本形式显示
        ui->textEditRev->setText(QString::fromUtf8(tmpQbyteString));
    }
}

⚠️ 注意:此函数重复粘贴了两次,实际项目中应确保只保留一份定义。此处仅为满足“保留源代码并添加注释”的需求而展示。


✅ 功能总结

操作描述
勾选 Hex 显示将文本转换为带空格分隔的 大写十六进制字符串 并显示
取消勾选 Hex 显示将界面上的十六进制字符串还原为原始文本显示
应用场景常用于串口调试助手、协议分析工具等需要切换 ASCII / HEX 显示的场合

以下是你提供的两个 Qt 槽函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

/**
 * 当点击 "btnhideTable" 按钮时触发的槽函数
 * 用于切换是否隐藏/显示 groupBoxTexts 面板
 */
void Widget::on_btnhideTable_clicked(bool checked)
{
    if(checked){
        // 如果按钮处于选中状态(即当前要隐藏面板)

        // 修改按钮文本为“拓展面板”
        ui->btnhideTable->setText("拓展面板");

        // 隐藏 groupBoxTexts 控件
        ui->groupBoxTexts->hide();
    }else{
        // 如果按钮处于未选中状态(即当前要显示面板)

        // 修改按钮文本为“隐藏面板”
        ui->btnhideTable->setText("隐藏面板");

        // 显示 groupBoxTexts 控件
        ui->groupBoxTexts->show();
    }
}


/**
 * 当点击 "btnHideHistory" 按钮时触发的槽函数
 * 用于切换是否隐藏/显示 groupBoxRecord 历史记录面板
 */
void Widget::on_btnHideHistory_clicked(bool checked)
{
    if(checked){
        // 如果按钮处于选中状态(即当前要隐藏历史记录面板)

        // 修改按钮文本为“显示历史”
        ui->btnHideHistory->setText("显示历史");

        // 隐藏 groupBoxRecord 控件
        ui->groupBoxRecord->hide();
    }else{
        // 如果按钮处于未选中状态(即当前要显示历史记录面板)

        // 修改按钮文本为“隐藏历史”
        ui->btnHideHistory->setText("隐藏历史");

        // 显示 groupBoxRecord 控件
        ui->groupBoxRecord->show();
    }
}

✅ 功能说明

函数名控件作用功能描述
on_btnhideTable_clickedbtnhideTable控制 groupBoxTexts 面板的显示与隐藏,并根据状态切换按钮文字
on_btnHideHistory_clickedbtnHideHistory控制 groupBoxRecord 面板的显示与隐藏,并根据状态切换按钮文字

这些函数通常用于实现界面中的“折叠/展开”功能,提升用户交互体验。

以下是你提供的 on_checkBHexDisplay_clicked 函数的完整注释版代码,保留原始逻辑和格式,仅添加详细中文注释:

void Widget::on_checkBHexDisplay_clicked(bool checked)
{
    if(checked){
        // 当复选框被选中时(启用十六进制显示模式)

        // 1. 从 textEditRev 控件中读取当前显示的纯文本内容
        QString tmp = ui->textEditRev->toPlainText();

        // 2. 将读取到的文本转换为 UTF-8 编码的字节数组,
        // 然后将该字节数组转换为对应的十六进制字符串表示(小写格式)
        QByteArray qtemp = tmp.toUtf8();     // 将 QString 转换为 QByteArray(UTF-8 格式)
        qtemp = qtemp.toHex();               // 将字节数组转换为十六进制字符串(如 "48656c6c6f")

        QString lastshow;
        tmp = QString::fromUtf8(qtemp);      // 将 QByteArray 类型的十六进制数据转回 QString

        // 3. 对转换后的十六进制字符串进行格式化处理:
        // 每两个字符一组进行分割,并在每组之间添加空格,提高可读性
        for (int i = 0; i < tmp.size(); i += 2) {
            lastshow += tmp.mid(i, 2) + " ";
        }

        // 最后将带有空格分隔的十六进制字符串转换为大写形式,
        // 并设置回 textEditRev 控件中以供显示
        ui->textEditRev->setText(lastshow.toUpper());
    }else{
        // 当复选框未被选中时(关闭十六进制显示,恢复为普通文本显示)

        // 1. 从 textEditRev 控件中读取当前显示的内容(假设是十六进制格式字符串)
        QString tmpHexString = ui->textEditRev->toPlainText();

        // 2. 将读取到的十六进制字符串转换为 UTF-8 编码的字节数组
        QByteArray tmpHexQBytearray = tmpHexString.toUtf8();

        // 使用 QByteArray::fromHex 方法将十六进制编码的字节数组转换回原始的二进制字节数组
        QByteArray tmpQbyteString = QByteArray::fromHex(tmpHexQBytearray);

        // 3. 将转换回的原始二进制数据转换为 QString 字符串,
        // 并重新设置回 textEditRev 控件中,以便以普通文本形式显示
        ui->textEditRev->setText(QString::fromUtf8(tmpQbyteString));
    }
}

✅ 功能总结

操作描述
勾选 Hex 显示将界面上的文本内容转换为 带空格分隔的大写十六进制字符串 并显示
取消勾选 Hex 显示将界面上的十六进制字符串还原为原始的 普通文本 形式显示
应用场景常用于串口调试助手、通信协议分析等需要切换 ASCII / HEX 显示的场合

此函数实现了用户界面中“十六进制显示”与“普通文本显示”的切换功能。

以下是你提供的两个函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

/**
 * 定时刷新时间显示的槽函数
 * 调用 getSysTime() 获取当前系统时间,并更新到界面上的 labelCurrentTime 控件
 */
void Widget::time_reflash()
{
    // 获取当前系统时间,并将结果保存在成员变量 myTime 中
    getSysTime();

    // 将获取到的时间字符串设置到界面上的 labelCurrentTime 标签中显示
    ui->labelCurrentTime->setText(myTime);
}


/**
 * 获取当前系统时间并格式化为指定格式的字符串(YYYY-MM-DD HH:MM:SS)
 */
void Widget::getSysTime()
{
    // 获取当前日期时间对象
    QDateTime currentTime = QDateTime::currentDateTime();

    // 提取当前日期部分
    QDate date = currentTime.date();

    // 从日期中提取年、月、日
    int year = date.year();
    int month = date.month();
    int day = date.day();

    // 提取当前时间部分
    QTime time = currentTime.time();
    int hour = time.hour();     // 小时
    int minute = time.minute(); // 分钟
    int second = time.second(); // 秒

    // 将时间信息格式化为 "YYYY-MM-DD HH:MM:SS" 字符串格式
    // 使用 arg 方法进行格式化,每个数字都保证至少两位,不足补零
    myTime = QString("%1-%2-%3 %4:%5:%6")
            .arg(year, 2, 10, QChar('0'))     // 年份(两位显示)
            .arg(month, 2, 10, QChar('0'))    // 月份(两位显示)
            .arg(day, 2, 10, QChar('0'))      // 日(两位显示)
            .arg(hour, 2, 10, QChar('0'))     // 小时(两位显示)
            .arg(minute, 2, 10, QChar('0'))   // 分钟(两位显示)
            .arg(second, 2, 10, QChar('0'));  // 秒(两位显示)
}

✅ 功能说明

函数名功能描述
time_reflash()触发时间刷新操作,调用 getSysTime() 更新界面时间标签
getSysTime()获取当前系统时间,并格式化为 "YYYY-MM-DD HH:MM:SS" 形式保存到 myTime 成员变量中

📌 使用场景

  • 常用于需要实时显示系统时间的界面控件中。
  • 可通过定时器(QTimer)定期调用 time_reflash() 来实现时间自动刷新功能。

以下是你提供的两个 Qt 槽函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

// 清除接收区域按钮点击事件处理函数
void Widget::on_btnrevClear_clicked()
{
    // 清空接收区文本编辑控件(textEditRev)中的所有内容
    ui->textEditRev->setText("");
}


// 保存接收数据按钮点击事件处理函数
void Widget::on_btnrevSave_clicked()
{
    // 弹出文件保存对话框,让用户选择保存路径和文件名
    // 默认路径为 "E:/QT/serialData.txt",默认文件名为 serialData.txt,支持 txt 格式
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    "E:/QT/serialData.txt",
                                                    tr("Text (*.txt)"));

    // 如果用户没有点击取消,即获取到了有效的文件路径
    if(fileName != NULL){
        // 创建 QFile 对象,用于操作目标文件
        QFile file(fileName);

        // 以只写模式和文本格式打开文件,如果打开失败则返回
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            return;

        // 创建 QTextStream 对象,用于向文件写入文本内容
        QTextStream out(&file);

        // 将接收区域(textEditRev)中的纯文本内容写入到文件中
        out << ui->textEditRev->toPlainText();

        // 关闭文件
        file.close();
    }
}

✅ 功能说明

函数名控件功能描述
on_btnrevClear_clicked()btnrevClear清空接收区域(textEditRev)中的所有内容
on_btnrevSave_clicked()btnrevSave将接收区域中的内容保存到用户指定的 .txt 文件中

以下是你提供的 on_btnCloseOrOpenSerial_clicked 函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

// 重载的 on_btnCloseOrOpenSerial_clicked 函数,带 bool 参数表示按钮状态
void Widget::on_btnCloseOrOpenSerial_clicked(bool checked)
{
    // 输出调试信息,显示按钮是否被选中
    qDebug() << "none checked" << checked;

    if(checked){
        // 当按钮被选中时(即尝试打开串口)

        // 1. 选择端口号
        serialPort->setPortName(ui->comboBox_serialNum->currentText());

        // 2. 配置波特率
        serialPort->setBaudRate(ui->comboBox_boautrate->currentText().toInt());

        // 3. 配置数据位
        serialPort->setDataBits(QSerialPort::DataBits(
                                    ui->comboBox_databit->currentText().toUInt()));

        // 4. 配置校验位
        switch (ui->comboBox_checkbit->currentIndex()) {
        case 0 :
            serialPort->setParity(QSerialPort::NoParity);
            break;
        case 2 :
            serialPort->setParity(QSerialPort::EvenParity);
            break;
        case 3 :
            serialPort->setParity(QSerialPort::OddParity);
            break;
        case 4 :
            serialPort->setParity(QSerialPort::SpaceParity);
            break;
        case 5 :
            serialPort->setParity(QSerialPort::MarkParity);
            break;
        default:
            serialPort->setParity(QSerialPort::UnknownParity);
            break;
        }

        // 5. 配置停止位
        serialPort->setStopBits(QSerialPort::StopBits(
                                    ui->comboBox_stopbit->currentText().toUInt()));

        // 6. 流控配置
        if(ui->comboBox_fileCon->currentText() == "None")
            serialPort->setFlowControl(QSerialPort::NoFlowControl);

        // 7. 尝试打开串口
        if(serialPort->open(QIODevice::ReadWrite)){
            qDebug() << "serial open successful";

            // 禁用相关配置项防止更改
            ui->comboBox_databit->setEnabled(false);
            ui->comboBox_boautrate->setEnabled(false);
            ui->comboBox_checkbit->setEnabled(false);
            ui->comboBox_fileCon->setEnabled(false);
            ui->comboBox_serialNum->setEnabled(false);
            ui->comboBox_stopbit->setEnabled(false);

            // 改变按钮文字为“关闭串口”
            ui->btnCloseOrOpenSerial->setText("关闭串口");

            // 启用发送按钮
            ui->btnSendContext->setEnabled(true);

            // 启用定时发送复选框
            ui->checkBSendInTime->setEnabled(true);

            ui->checkBSendNewLine->setEnabled(true);
            ui->checkBHexSend->setEnabled(true);
            ui->labelSendStatus->setText(ui->comboBox_serialNum->currentText() + " isOpen!");

        }else{
            // 如果打开失败,弹出错误提示框
            QMessageBox msgBox;
            msgBox.setWindowTitle("打开串口错误");
            msgBox.setText("打开失败!串口可能被占用或者已经被拔出!");
            msgBox.exec();
        }
    }else{
        // 当按钮未被选中时(即尝试关闭串口)

        // 关闭串口
        serialPort->close();

        // 改变按钮文字为“打开串口”
        ui->btnCloseOrOpenSerial->setText("打开串口");

        // 启用所有配置项
        ui->comboBox_databit->setEnabled(true);
        ui->comboBox_boautrate->setEnabled(true);
        ui->comboBox_checkbit->setEnabled(true);
        ui->comboBox_fileCon->setEnabled(true);
        ui->comboBox_serialNum->setEnabled(true);
        ui->comboBox_stopbit->setEnabled(true);

        // 禁用发送按钮
        ui->btnSendContext->setEnabled(false);

        // 取消勾选定时发送复选框,并禁用该选项
        ui->checkBSendInTime->setEnabled(false);
        ui->checkBSendInTime->setCheckState(Qt::Unchecked);

        // 停止定时器
        timer->stop();

        // 启用时间间隔输入框和发送内容输入框
        ui->lineEditTimeeach->setEnabled(true);
        ui->lineEditSendContext->setEnabled(true);

        ui->checkBSendNewLine->setEnabled(false);
        ui->checkBHexSend->setEnabled(false);
        ui->labelSendStatus->setText(ui->comboBox_serialNum->currentText() + " isClose!");
    }
}

✅ 功能说明

操作描述
打开串口设置串口参数(端口号、波特率、数据位、校验位、停止位、流控),并尝试打开串口;成功后更新界面状态
关闭串口关闭串口,并恢复界面初始状态以便重新配置

此函数实现了串口通信中的基本操作:打开与关闭串口,并根据串口状态动态调整用户界面元素的可用性。这对于需要频繁操作串口的应用程序非常有用。

以下是你提供的 on_SerialData_readyToRead() 函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

// 当串口有可读数据时触发此槽函数
void Widget::on_SerialData_readyToRead()
{
    // 读取串口缓冲区中所有可读的数据(一次性读取)
    QString revMassage = serialPort->readAll();

    // 如果收到的数据非空,则进行后续处理
    if(revMassage != NULL){
        // 如果勾选了“自动换行”选项,则在接收到的消息末尾追加换行符
        if(ui->checkBLine->isChecked())
            revMassage.append("\r\n");

        // 检查是否勾选了“以十六进制显示”选项
        if(ui->checkBHexDisplay->isChecked()){
            // 将收到的消息字符串转换为 UTF-8 编码的字节数组,
            // 再将其转换为大写的十六进制表示形式
            QByteArray tmpHexString = revMassage.toUtf8().toHex().toUpper();

            // 获取当前 textEditRev 控件中的内容(假设是已有的十六进制格式字符串)
            QString tmpStringHex = ui->textEditRev->toPlainText();

            // 将旧的十六进制字符串与新的十六进制数据拼接在一起
            tmpHexString = tmpStringHex.toUtf8() + tmpHexString;

            // 最后将拼接后的完整十六进制字符串设置回 textEditRev 控件中显示
            ui->textEditRev->setText(QString::fromUtf8(tmpHexString));
        }else{
            // 如果没有勾选“以十六进制显示”选项,则根据时间戳选项进一步处理

            if(ui->checkBrevTime->checkState() == Qt::Unchecked){
                // 若未勾选“显示时间戳”选项,则直接将接收到的内容插入到接收框中
                ui->textEditRev->insertPlainText(revMassage);
            }else if (ui->checkBrevTime->checkState() == Qt::Checked) {
                // 若勾选了“显示时间戳”选项,则先获取当前系统时间,
                // 然后将时间和消息一起插入到接收框中
                getSysTime();
                ui->textEditRev->insertPlainText("【" + myTime + "】" + revMassage);
            }
        }

        // 更新总接收字节数统计
        readCntTotal += revMassage.size();

        // 在界面上显示更友好的接收字节数统计信息
        ui->labelRevcnt->setText("Received:" + QString::number(readCntTotal));

        // 打印收到的消息内容(调试用,已被注释)
        // qDebug() << "getMassage:" << revMassage; // 打印收到的消息
    }
}

✅ 功能说明

条件描述
checkBLine 被勾选接收数据后自动添加换行符 \r\n
checkBHexDisplay 被勾选接收数据显示为 大写十六进制格式
checkBrevTime 被勾选接收数据前加上当前系统时间戳

该函数实现了当串口接收到数据时的实时处理与显示功能,支持多种显示模式切换,适用于串口调试助手等场景。

以下是你提供的 on_btnSendContext_clicked() 函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

// 发送按钮点击事件处理函数:发送文本框中的内容
void Widget::on_btnSendContext_clicked()
{
    int writeCnt = 0; // 用于记录实际写入串口的字节数

    // 将发送文本框中的内容转换为本地编码(如 GBK 或 UTF-8)的 C 风格字符串
    const char* sendData = ui->lineEditSendContext->text().toLocal8Bit().constData();

    // 判断是否启用“十六进制发送”模式
    if(ui->checkBHexSend->isChecked()){
        // 获取发送框中的文本内容
        QString tmp = ui->lineEditSendContext->text();

        // 1. 检查输入长度是否为偶数(因为每两个字符表示一个字节)
        QByteArray tmpArray = tmp.toLocal8Bit();
        if(tmpArray.size() % 2 != 0){
            // 输入长度不合法,设置状态标签提示错误
            ui->labelSendStatus->setText("Error Input!");
            return;
        }

        // 2. 检查输入是否全部为十六进制字符(0-9, a-f, A-F)
        for(char c : tmpArray){
            if(!std::isxdigit(c)){
                // 存在非十六进制字符,提示错误
                ui->labelSendStatus->setText("Error Input!");
                return;
            }
        }

        // 如果勾选了“自动换行”,则在数据末尾追加 \r\n
        if(ui->checkBSendNewLine->isChecked())
            tmpArray.append("\r\n");

        // 3. 将十六进制字符串转换为实际字节数据进行发送
        QByteArray arraySend = QByteArray::fromHex(tmpArray);
        writeCnt = serialPort->write(arraySend); // 写入串口
    }else{
        // 未勾选十六进制发送,按普通文本发送

        // 如果勾选了“自动换行”,则构造包含换行符的数据包
        if(ui->checkBSendNewLine->isChecked()){
            QByteArray arraySendData(sendData, strlen(sendData));
            arraySendData.append("\r\n");
            writeCnt = serialPort->write(arraySendData); // 发送带换行的内容
        }else{
            // 不带换行直接发送原始数据
            writeCnt = serialPort->write(sendData);
        }
    }

    // 判断写入是否成功
    if(writeCnt == -1){
        // 写入失败,更新发送状态为错误
        ui->labelSendStatus->setText("SendError!");
    }else{
        // 写入成功,更新总发送字节数统计
        writeCntTotal += writeCnt;

        // 打印调试信息
        qDebug() << "send OK" << sendData;

        // 更新发送状态为“发送成功”
        ui->labelSendStatus->setText("SendOK!");

        // 显示更友好的发送字节数统计格式
        ui->labelSendcnt->setText("Sent:" + QString::number(writeCntTotal));

        // 如果本次发送的内容与上次不同,则追加到发送记录中
        if(strcmp(sendData, sendBak.toStdString().c_str()) != 0){
            // 在记录框中追加发送的内容
            ui->textEditRecord->append(sendData);

            // 保存当前发送内容作为下一次比较的依据
            sendBak = QString::fromUtf8(sendData);
        }
    }
}

✅ 功能说明

条件描述
checkBHexSend 被勾选表示用户希望以 十六进制格式 发送数据
checkBSendNewLine 被勾选发送数据后自动附加 \r\n 换行符
输入合法性检查十六进制发送时必须是偶数长度且全为十六进制字符
自动记录功能如果新发送内容不同于上一次,则自动追加到发送记录区域

此函数实现了串口通信中“手动发送数据”的核心功能,并支持多种发送模式切换,适用于串口调试助手等应用场景。

以下是你提供的 Widget 构造函数的完整注释版代码,保留原始逻辑和格式,仅添加详细的中文注释:

// 构造函数:初始化 Widget 界面并配置串口相关设置
Widget::Widget(QWidget *parent)
    : QWidget(parent)  // 调用父类构造函数
    , ui(new Ui::Widget)  // 创建 UI 对象
{
    // 从 .ui 文件加载界面布局及控件
    ui->setupUi(this);

    // 设置主布局为 gridLayoutGlobal(在.ui中定义)
    this->setLayout(ui->gridLayoutGlobal);

    // 初始化发送和接收字节计数器为0
    writeCntTotal = 0;
    readCntTotal = 0;

    // 初始串口状态为关闭状态
    serialStatus = false;

    // 默认禁用发送按钮(因为串口未打开)
    ui->btnSendContext->setEnabled(false);

    // 默认禁用定时发送复选框(checkBSendInTime)
    ui->checkBSendInTime->setEnabled(false);

    // 同时禁用“自动换行”和“十六进制发送”选项
    ui->checkBSendNewLine->setEnabled(false);
    ui->checkBHexSend->setEnabled(false);

    // 创建一个新的 QSerialPort 对象用于串口通信
    serialPort = new QSerialPort(this);

    // 创建定时器用于刷新时间显示
    QTimer *getSysTimeTimer = new QTimer(this);

    // 连接定时器超时信号到 time_reflash 槽函数(每秒刷新一次时间)
    connect(getSysTimeTimer, SIGNAL(timeout()), this, SLOT(time_reflash()));

    // 启动定时器,间隔为1000毫秒(即1秒)
    getSysTimeTimer->start(1000);

    // 创建定时器对象,用于实现定时发送功能
    timer = new QTimer(this);

    // 连接串口的 readyRead 信号到槽函数 on_SerialData_readyToRead,
    // 当串口有可读数据时会触发该槽函数
    connect(serialPort, &QSerialPort::readyRead, this, &Widget::on_SerialData_readyToRead);

    // 连接定时器超时信号到 lambda 表达式,用于触发发送按钮点击事件
    connect(timer, &QTimer::timeout, [=](){
        on_btnSendContext_clicked();
    });

    // 设置波特率下拉框默认选中第7个选项(索引从0开始,第6个元素)
    ui->comboBox_boautrate->setCurrentIndex(6);

    // 设置数据位下拉框默认选中第4个选项(索引从0开始,第3个元素)
    ui->comboBox_databit->setCurrentIndex(3);

    // 获取系统当前所有可用串口信息
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();

    // 遍历所有可用串口,并将串口号添加到 comboBox_serialNum 下拉框中
    for (QSerialPortInfo serialInfo : serialList) {
        qDebug() << serialInfo.portName(); // 打印串口号(调试信息)
        ui->comboBox_serialNum->addItem(serialInfo.portName()); // 添加到下拉列表
    }

    // 设置初始发送状态标签文本为 "串口号NotOpen!"
    ui->labelSendStatus->setText(ui->comboBox_serialNum->currentText() + "NotOpen!");
}

✅ 功能说明

功能描述
界面初始化加载 .ui 文件中的界面布局和控件
计数器初始化初始化发送与接收字节数统计变量
控件状态初始化初始状态下禁用发送、定时发送等控件
串口对象创建创建 QSerialPort 实例用于串口通信
时间刷新定时器每秒更新一次界面上的时间显示
数据接收连接当串口有数据可读时,调用 on_SerialData_readyToRead 函数
定时发送连接使用定时器定时触发发送按钮点击事件
默认参数设置波特率和数据位设置为常用默认值
串口枚举自动检测并列出当前系统中所有可用串口

此构造函数完成了串口调试助手的基本初始化工作,包括界面、控件状态、串口设备枚举以及基础通信机制的搭建。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

范纹杉好好生活工作

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值