项目开发进度表:
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操作系统
安装步骤
- 确保已安装Qt开发环境,建议使用Qt 5.12.12版本,并勾选MinGW 32-bit编译器组件
- 克隆或下载本项目到本地
- 打开Qt Creator,通过"文件"->"打开文件或项目"选择项目根目录下的
serial-debugging-assistant.pro
文件 - 选择构建套件(推荐:Desktop Qt 5.12.12 MinGW 32-bit)
- 点击"构建"->"构建项目"编译项目
- 编译完成后,点击"运行"按钮启动应用程序
使用方法
基本操作
- 启动应用程序,界面将显示所有可用串口
- 在串口配置区域选择合适的参数(波特率、数据位、校验位、停止位、流控)
- 点击"打开串口"按钮连接设备
- 在发送区输入要发送的数据
- 点击"发送"按钮发送数据
- 接收的数据将显示在接收区,并自动更新接收字节数统计
高级功能
- 定时发送:勾选"定时发送"复选框,设置时间间隔,系统将自动循环发送当前输入框中的数据
- 多文本发送:在右侧多文本区域预设常用指令,点击对应编号按钮快速发送
- 历史记录:所有发送的指令会自动保存到历史记录区,方便查看和复用
- 数据保存:点击"保存接收"按钮可将接收区数据保存到文件
- 清空接收:点击"清空接收"按钮可清除接收区内容
项目结构
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 # 界面布局文件
贡献指南
- Fork本仓库
- 创建特性分支 (
git checkout -b feature/xxx
) - 提交代码更改 (
git commit -m 'Add some feature'
) - 推送到分支 (
git push origin feature/xxx
) - 创建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_serialNum
的 QComboBox
控件中。这段代码看起来是正确的,但有几个方面需要注意以确保一切正常工作:
1. 确认 UI 文件中的控件名称
首先,确保在你的 .ui
文件(即 widget.ui
)中确实有一个名为 comboBox_serialNum
的 QComboBox
控件。如果这个控件不存在或名称不匹配,程序将在运行时抛出错误。
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. 检查串口是否打开成功
你当前只打印了一句调试信息:
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 | ✅ 因为没有收到数据 |
✅ 下一步建议
- 加日志输出:每一步都打印调试信息;
- 确认串口打开状态;
- 使用串口调试助手验证设备通信逻辑;
- 尝试发送固定命令测试设备是否响应;
- 确保使用正确的编码格式(如 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));
}
}
注释解释
-
当复选框被选中时:
- 读取内容:首先从
textEditRev
控件中获取当前显示的所有文本。 - 转换为十六进制:将获取到的文本转换为 UTF-8 编码的字节数组,然后进一步将其转换为十六进制字符串表示。
- 更新显示:最后,将转换后的十六进制字符串重新设置为
textEditRev
的文本。
- 读取内容:首先从
-
当复选框未被选中时:
- 读取内容:从
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_clicked | btnhideTable | 控制 groupBoxTexts 面板的显示与隐藏,并根据状态切换按钮文字 |
on_btnHideHistory_clicked | btnHideHistory | 控制 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 函数 |
定时发送连接 | 使用定时器定时触发发送按钮点击事件 |
默认参数设置 | 波特率和数据位设置为常用默认值 |
串口枚举 | 自动检测并列出当前系统中所有可用串口 |
此构造函数完成了串口调试助手的基本初始化工作,包括界面、控件状态、串口设备枚举以及基础通信机制的搭建。