文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT Widget 开发(一)工程文件结构-CSDN博客
开源 C++ QT Widget 开发(二)基本控件应用-CSDN博客
开源 C++ QT Widget 开发(三)图表--波形显示器-CSDN博客
开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-CSDN博客
开源 C++ QT Widget 开发(五)通讯--串口调试-CSDN博客
开源 C++ QT Widget 开发(六)通讯--TCP调试-CSDN博客
推荐链接:
开源 java android app 开发(一)开发环境的搭建-CSDN博客
开源 java android app 开发(二)工程文件结构-CSDN博客
开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客
开源 java android app 开发(四)GUI界面重要组件-CSDN博客
开源 java android app 开发(五)文件和数据库存储-CSDN博客
开源 java android app 开发(六)多媒体使用-CSDN博客
开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客
开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客
开源 java android app 开发(九)后台之线程和服务-CSDN博客
开源 java android app 开发(十)广播机制-CSDN博客
开源 java android app 开发(十一)调试、发布-CSDN博客
开源 java android app 开发(十二)封库.aar-CSDN博客
推荐链接:
开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客
开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客
本章主要内容:实现了一个串口调试工具,参数可设,支持实时数据收发、格式转换、参数配置及通信过程监控等功能。可以广泛应用于工业控制、单片机开发、智能设备调试等场景,可通过调节波特率(110bps至256000bps)、校验方式等参数实现高速串口通讯。
一、代码分析
1.1 串口配置模块
// 串口参数配置
QComboBox *portComboBox; // 串口选择
QComboBox *baudComboBox; // 波特率选择(9600-115200)
QComboBox *stopBitsComboBox; // 停止位(1, 1.5, 2)
QComboBox *parityComboBox; // 校验位(无, 奇校验, 偶校验)
数据接收模块
cpp
QTextEdit *receiveEdit; // 接收数据显示区域
QCheckBox *hexDisplayCheckBox; // 16进制显示开关
数据发送模块
cpp
QTextEdit *sendEdit; // 发送数据输入区域
QCheckBox *hexSendCheckBox; // 16进制发送开关
1.2 核心功能实现分析
- 串口管理
cpp
void MainWindow::onOpenButtonClicked()
{
// 实现串口的打开/关闭切换
// 配置串口参数:端口名、波特率、数据位、停止位、校验位
}
- 数据发送处理
cpp
void MainWindow::onSendButtonClicked()
{
// 支持两种发送模式:
// - 文本模式:直接发送UTF-8文本
// - 16进制模式:将输入的16进制字符串转换为字节数据
// 包含数据验证:16进制数据长度校验、字符合法性检查
}
- 数据接收处理
cpp
void MainWindow::onSerialReadyRead()
{
// 支持两种显示模式:
// - 文本模式:直接显示接收到的文本
// - 16进制模式:将字节数据转换为16进制字符串显示
// 自动滚动到底部功能
}
二、所有源码
2.1 mainwindow.h源码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QComboBox>
#include <QTextEdit>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QGroupBox>
#include <QVBoxLayout>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onOpenButtonClicked();
void onRefreshButtonClicked();
void onSendButtonClicked();
void onSerialReadyRead();
void onClearReceiveButtonClicked();
void onClearSendButtonClicked();
private:
void createSerialConfigGroup();
void createReceiveGroup();
void createSendGroup();
void initSerialPort();
void refreshSerialPorts();
void connectSignals();
// 串口配置控件
QComboBox *portComboBox;
QComboBox *baudComboBox;
QComboBox *stopBitsComboBox;
QComboBox *parityComboBox;
QPushButton *refreshButton;
QPushButton *openButton;
QLabel *statusLabel;
// 接收区域控件
QTextEdit *receiveEdit;
QCheckBox *hexDisplayCheckBox;
QPushButton *clearReceiveButton;
// 发送区域控件
QTextEdit *sendEdit;
QCheckBox *hexSendCheckBox;
QPushButton *clearSendButton;
QPushButton *sendButton;
// 串口对象
QSerialPort *serialPort;
// 布局和容器
QWidget *centralWidget;
QVBoxLayout *mainLayout;
QGroupBox *configGroup;
QGroupBox *receiveGroup;
QGroupBox *sendGroup;
};
#endif // MAINWINDOW_H
2.2 mainwindow.cpp源码
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QScrollBar>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 设置窗口大小
this->resize(1000, 600);
this->setWindowTitle("QT串口调试助手");
// 创建中央部件
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
// 主布局
mainLayout = new QVBoxLayout(centralWidget);
// 创建各个功能区域
createSerialConfigGroup();
createReceiveGroup();
createSendGroup();
// 将各个区域添加到主布局
mainLayout->addWidget(configGroup);
mainLayout->addWidget(receiveGroup);
mainLayout->addWidget(sendGroup);
// 初始化串口
initSerialPort();
// 刷新可用串口
refreshSerialPorts();
// 连接信号槽
connectSignals();
}
MainWindow::~MainWindow()
{
if(serialPort && serialPort->isOpen())
{
serialPort->close();
}
}
void MainWindow::createSerialConfigGroup()
{
configGroup = new QGroupBox("串口配置");
QHBoxLayout *configLayout = new QHBoxLayout(configGroup);
// 串口选择
QLabel *portLabel = new QLabel("串口:");
portComboBox = new QComboBox();
configLayout->addWidget(portLabel);
configLayout->addWidget(portComboBox);
// 波特率选择
QLabel *baudLabel = new QLabel("波特率:");
baudComboBox = new QComboBox();
baudComboBox->addItems(QStringList() << "9600" << "19200" << "38400" << "57600" << "115200");
baudComboBox->setCurrentText("115200");
configLayout->addWidget(baudLabel);
configLayout->addWidget(baudComboBox);
// 停止位选择
QLabel *stopBitsLabel = new QLabel("停止位:");
stopBitsComboBox = new QComboBox();
stopBitsComboBox->addItems(QStringList() << "1" << "1.5" << "2");
configLayout->addWidget(stopBitsLabel);
configLayout->addWidget(stopBitsComboBox);
// 校验位选择
QLabel *parityLabel = new QLabel("校验位:");
parityComboBox = new QComboBox();
parityComboBox->addItems(QStringList() << "无" << "奇校验" << "偶校验");
configLayout->addWidget(parityLabel);
configLayout->addWidget(parityComboBox);
// 刷新按钮
refreshButton = new QPushButton("刷新");
configLayout->addWidget(refreshButton);
// 打开/关闭按钮
openButton = new QPushButton("打开串口");
configLayout->addWidget(openButton);
// 状态标签
statusLabel = new QLabel("串口未打开");
configLayout->addWidget(statusLabel);
configLayout->addStretch();
}
void MainWindow::createReceiveGroup()
{
receiveGroup = new QGroupBox("接收数据");
QVBoxLayout *receiveLayout = new QVBoxLayout(receiveGroup);
// 接收文本框
receiveEdit = new QTextEdit();
receiveEdit->setReadOnly(true);
receiveLayout->addWidget(receiveEdit);
// 接收选项
QHBoxLayout *receiveOptionsLayout = new QHBoxLayout();
hexDisplayCheckBox = new QCheckBox("16进制显示");
clearReceiveButton = new QPushButton("清空接收");
receiveOptionsLayout->addWidget(hexDisplayCheckBox);
receiveOptionsLayout->addWidget(clearReceiveButton);
receiveOptionsLayout->addStretch();
receiveLayout->addLayout(receiveOptionsLayout);
}
void MainWindow::createSendGroup()
{
sendGroup = new QGroupBox("发送数据");
QVBoxLayout *sendLayout = new QVBoxLayout(sendGroup);
// 发送文本框
sendEdit = new QTextEdit();
sendEdit->setMaximumHeight(100);
sendLayout->addWidget(sendEdit);
// 发送选项
QHBoxLayout *sendOptionsLayout = new QHBoxLayout();
hexSendCheckBox = new QCheckBox("16进制发送");
clearSendButton = new QPushButton("清空发送");
sendButton = new QPushButton("发送");
sendOptionsLayout->addWidget(hexSendCheckBox);
sendOptionsLayout->addWidget(clearSendButton);
sendOptionsLayout->addWidget(sendButton);
sendOptionsLayout->addStretch();
sendLayout->addLayout(sendOptionsLayout);
}
void MainWindow::initSerialPort()
{
serialPort = new QSerialPort(this);
}
void MainWindow::refreshSerialPorts()
{
portComboBox->clear();
QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
for(const QSerialPortInfo &port : ports)
{
portComboBox->addItem(port.portName());
}
}
void MainWindow::connectSignals()
{
connect(openButton, &QPushButton::clicked, this, &MainWindow::onOpenButtonClicked);
connect(refreshButton, &QPushButton::clicked, this, &MainWindow::onRefreshButtonClicked);
connect(sendButton, &QPushButton::clicked, this, &MainWindow::onSendButtonClicked);
connect(clearReceiveButton, &QPushButton::clicked, this, &MainWindow::onClearReceiveButtonClicked);
connect(clearSendButton, &QPushButton::clicked, this, &MainWindow::onClearSendButtonClicked);
connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::onSerialReadyRead);
}
void MainWindow::onOpenButtonClicked()
{
if(serialPort->isOpen())
{
serialPort->close();
openButton->setText("打开串口");
statusLabel->setText("串口已关闭");
}
else
{
// 配置串口参数
serialPort->setPortName(portComboBox->currentText());
serialPort->setBaudRate(baudComboBox->currentText().toInt());
// 设置数据位
serialPort->setDataBits(QSerialPort::Data8);
// 设置停止位
QString stopBits = stopBitsComboBox->currentText();
if(stopBits == "1") serialPort->setStopBits(QSerialPort::OneStop);
else if(stopBits == "1.5") serialPort->setStopBits(QSerialPort::OneAndHalfStop);
else if(stopBits == "2") serialPort->setStopBits(QSerialPort::TwoStop);
// 设置校验位
QString parity = parityComboBox->currentText();
if(parity == "无") serialPort->setParity(QSerialPort::NoParity);
else if(parity == "奇校验") serialPort->setParity(QSerialPort::OddParity);
else if(parity == "偶校验") serialPort->setParity(QSerialPort::EvenParity);
// 打开串口
if(serialPort->open(QIODevice::ReadWrite))
{
openButton->setText("关闭串口");
statusLabel->setText("串口已打开: " + portComboBox->currentText());
}
else
{
QMessageBox::critical(this, "错误", "无法打开串口: " + serialPort->errorString());
}
}
}
void MainWindow::onRefreshButtonClicked()
{
refreshSerialPorts();
}
void MainWindow::onSendButtonClicked()
{
if(!serialPort->isOpen())
{
QMessageBox::warning(this, "警告", "请先打开串口");
return;
}
QString sendText = sendEdit->toPlainText();
if(sendText.isEmpty()) return;
QByteArray sendData;
if(hexSendCheckBox->isChecked())
{
// 16进制发送
sendText = sendText.trimmed();
sendText.replace(" ", "");
if(sendText.length() % 2 != 0)
{
QMessageBox::warning(this, "警告", "16进制数据长度必须为偶数");
return;
}
bool ok;
for(int i = 0; i < sendText.length(); i += 2)
{
QString byteStr = sendText.mid(i, 2);
uint8_t byte = byteStr.toUShort(&ok, 16);
if(!ok)
{
QMessageBox::warning(this, "警告", "包含非法的16进制字符");
return;
}
sendData.append(byte);
}
}
else
{
// 文本发送
sendData = sendText.toUtf8();
}
// 发送数据
serialPort->write(sendData);
}
void MainWindow::onSerialReadyRead()
{
QByteArray data = serialPort->readAll();
if(hexDisplayCheckBox->isChecked())
{
// 16进制显示
QString hexData;
for(int i = 0; i < data.size(); i++)
{
hexData += QString("%1 ").arg((uint8_t)data[i], 2, 16, QLatin1Char('0')).toUpper();
}
receiveEdit->insertPlainText(hexData);
}
else
{
// 文本显示
QString text = QString::fromUtf8(data);
receiveEdit->insertPlainText(text);
}
// 自动滚动到底部
QScrollBar *scrollbar = receiveEdit->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
void MainWindow::onClearReceiveButtonClicked()
{
receiveEdit->clear();
}
void MainWindow::onClearSendButtonClicked()
{
sendEdit->clear();
}
2.3 .prog工程文件
QT += core gui
QT += core gui serialport widgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
三、显示效果
