当前位置: 首页 > news >正文

开源 C++ QT QML 开发(十)通讯--串口

           文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 C++ QT QML 开发(一)基本介绍

开源 C++ QT QML 开发(二)工程结构

开源 C++ QT QML 开发(三)常用控件

开源 C++ QT QML 开发(四)复杂控件--Listview

开源 C++ QT QML 开发(五)复杂控件--Gridview

开源 C++ QT QML 开发(六)自定义控件--波形图

开源 C++ QT QML 开发(七)自定义控件--仪表盘

开源 C++ QT QML 开发(八)自定义控件--圆环

开源 C++ QT QML 开发(九)文件--文本和二进制

开源 C++ QT QML 开发(十)通讯--串口

开源 C++ QT QML 开发(十一)通讯--TCP服务器端

开源 C++ QT QML 开发(十二)通讯--TCP客户端

推荐链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

本章节主要内容是:实现了一个串口调试工具,可以对串口参数进行配置,可以接收和发送文本和十六进制数据。

1.代码分析

2.所有源码

3.效果演示

一、代码分析

1. qml代码分析
ApplicationWindow 根元素
 

ApplicationWindow {id: windowwidth: 1000height: 800title: "串口调试工具 - Qt 5.12"visible: true


创建主应用程序窗口

设置固定尺寸 1000x800

定义窗口标题和可见性

颜色主题系统
 

// 定义颜色主题
property color primaryColor: "#3498db"      // 主色调 - 蓝色
property color secondaryColor: "#2ecc71"    // 次要色调 - 绿色
property color accentColor: "#e74c3c"       // 强调色 - 红色
property color backgroundColor: "#f8f9fa"   // 背景色
property color cardColor: "#ffffff"         // 卡片背景色
property color textColor: "#2c3e50"         // 主要文字颜色
property color subTextColor: "#7f8c8d"      // 次要文字颜色
property color borderColor: "#bdc3c7"       // 边框颜色
property color successColor: "#27ae60"      // 成功颜色
property color warningColor: "#f39c12"      // 警告颜色
property color errorColor: "#e74c3c"        // 错误颜色


统一的设计系统,便于维护和修改

语义化颜色命名,提高代码可读性

背景设置
 

Rectangle {anchors.fill: parentcolor: backgroundColor
}


设置整个窗口的背景颜色

anchors.fill: parent 确保背景填充整个窗口

2. 串口管理器实例化
 

SerialPortManager {id: serialPort
}


创建 C++ SerialPortManager 类的 QML 实例

id: serialPort 用于在 QML 中引用该对象

3. 主布局结构
ColumnLayout 主容器
 

ColumnLayout {anchors.fill: parentanchors.margins: 15spacing: 12


垂直布局管理器,包含所有界面元素

anchors.fill: parent 填充整个窗口

设置边距和组件间距

4. 串口配置区域详细分析
GroupBox 容器
 

GroupBox {title: "📡 串口配置"Layout.fillWidth: truebackground: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 8}label: Label {text: parent.titlecolor: primaryColorfont.bold: truefont.pixelSize: 14padding: 5}


创建分组框,包含所有串口配置控件

自定义背景和边框样式

自定义标题标签样式

GridLayout 网格布局
 

GridLayout {columns: 6  // 6列网格width: parent.widthrowSpacing: 8columnSpacing: 8


6列网格布局,整齐排列配置控件

设置行间距和列间距

端口选择 ComboBox
 

ComboBox {id: portComboBoxLayout.fillWidth: truemodel: serialPort.portList  // 绑定到C++的端口列表background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}onModelChanged: {if (model.length > 0 && currentIndex === -1) {currentIndex = 0  // 自动选择第一个可用端口}}
}


动态绑定到 C++ 的端口列表

自动选择第一个可用端口

自定义样式参数选择 ComboBox 组

// 波特率选择

ComboBox {id: baudRateComboBoxmodel: ["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]currentIndex: 3 // 默认选择9600
}// 校验位选择(包含值映射)
ComboBox {id: parityComboBoxmodel: ["无", "奇校验", "偶校验", "标记", "空格"]property var parityValues: [0, 3, 2, 1, 4]  // 映射到C++枚举值
}


预定义常用参数选项

使用属性存储枚举值映射

设置合理的默认值

连接按钮
 

Button {id: connectButtontext: serialPort.isConnected ? "🔌 断开连接" : "🔗 连接"Layout.columnSpan: 2Layout.fillWidth: truebackground: Rectangle {color: serialPort.isConnected ? accentColor : secondaryColorradius: 6}onClicked: {if (serialPort.isConnected) {serialPort.disconnectSerialPort()} else {if (portComboBox.currentText) {serialPort.connectSerialPort(portComboBox.currentText,parseInt(baudRateComboBox.currentText),parseInt(dataBitsComboBox.currentText),parityComboBox.parityValues[parityComboBox.currentIndex],stopBitsComboBox.stopBitsValues[stopBitsComboBox.currentIndex],flowControlComboBox.flowControlValues[flowControlComboBox.currentIndex])}}}
}


动态文本:根据连接状态改变按钮文字

动态颜色:连接/断开状态使用不同颜色

参数传递:收集所有配置参数传递给 C++ 函数

空值检查:确保选择了端口

5. 发送数据区域
模式选择 CheckBox

CheckBox {id: sendHexCheckBoxtext: "十六进制发送"contentItem: Text {text: sendHexCheckBox.textcolor: textColorfont.bold: true}
}


控制发送数据的格式(文本/十六进制)

自定义文本样式

发送文本区域
 

ScrollView {Layout.fillWidth: trueLayout.preferredHeight: 80background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}TextArea {id: sendTextAreaplaceholderText: "请输入要发送的数据..."placeholderTextColor: subTextColorwrapMode: TextEdit.Wrapcolor: textColor}
}


可滚动的文本输入区域

设置占位符文本

支持自动换行

发送控制按钮组
 

RowLayout {Button {text: "🚀 发送"onClicked: {serialPort.sendData(sendTextArea.text, sendHexCheckBox.checked)}}Button {text: "🗑️ 清空发送"onClicked: sendTextArea.clear()}
}


发送按钮:传递文本内容和格式模式

清空按钮:清除输入框内容

6. 状态显示
 

Label {text: "状态: " + serialPort.statusMessagecolor: {if (serialPort.isConnected) return successColorelse if (serialPort.statusMessage.includes("错误") ||serialPort.statusMessage.includes("失败")) return errorColorelse return textColor}
}


动态状态文本:显示当前操作状态

智能颜色切换:

连接成功:绿色

错误/失败:红色

其他状态:默认颜色

7. 接收数据区域
接收显示区域
 

ScrollView {Layout.fillWidth: trueLayout.fillHeight: truebackground: Rectangle {color: "#2c3e50"  // 深色背景便于阅读border.color: borderColorborder.width: 1radius: 4}TextArea {id: receiveTextAreatext: serialPort.receivedData  // 绑定到C++接收数据wrapMode: TextEdit.WrapreadOnly: truefont.family: "Consolas, 'Courier New', monospace"  // 等宽字体font.pixelSize: 12selectByMouse: true  // 允许选择文本color: "#ecf0f1"     // 浅色文字}
}


数据绑定:自动更新显示接收到的数据

等宽字体:便于对齐和阅读

深色主题:减少长时间使用的视觉疲劳

文本选择:允许用户复制接收到的数据

接收统计和控制
 

RowLayout {Label {text: "📊 接收字节数: " + receiveTextArea.text.length}CheckBox {id: autoScrollCheckBoxtext: "自动滚动"checked: true  // 默认开启自动滚动}Button {text: "💾 保存数据"onClicked: {console.log("保存数据功能待实现")}}
}


字节统计:实时显示接收数据长度

自动滚动:控制是否自动滚动到最新内容

保存功能:预留数据保存接口

8. 状态栏
 

footer: ToolBar {background: Rectangle {color: primaryColor}RowLayout {anchors.fill: parentLabel {text: "🔧 串口调试工具 v1.0 | 就绪"color: "white"font.bold: true}Item { Layout.fillWidth: true }  // 占位空间Label {text: new Date().toLocaleString(Qt.locale(), "yyyy-MM-dd hh:mm:ss")color: "white"}}
}


应用信息:显示版本和状态

实时时钟:显示当前时间

主色调背景:与整体设计保持一致

9. 数据绑定和交互机制
属性绑定
 

model: serialPort.portList           // 自动更新串口列表
text: serialPort.receivedData        // 自动更新接收数据
checked: serialPort.isConnected      // 自动更新连接状态


条件渲染
 

text: serialPort.isConnected ? "断开连接" : "连接"
color: serialPort.isConnected ? accentColor : secondaryColor


事件处理
 

onClicked: { /* 处理点击 */ }
onModelChanged: { /* 响应模型变化 */ }


SerialPortManager.cpp分析
1. 构造函数 SerialPortManager::SerialPortManager
 

SerialPortManager::SerialPortManager(QObject *parent): QObject(parent), m_serialPort(new QSerialPort(this))  // 创建串口对象, m_hexDisplay(false)                  // 初始化显示模式为文本
{// 初始化定时器,定时刷新串口列表m_portRefreshTimer = new QTimer(this);connect(m_portRefreshTimer, &QTimer::timeout, this, &SerialPortManager::refreshPorts);m_portRefreshTimer->start(2000); // 每2秒刷新一次// 连接串口信号到槽函数connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortManager::onReadyRead);connect(m_serialPort, &QSerialPort::errorOccurred, this, &SerialPortManager::onErrorOccurred);refreshPorts(); // 初始刷新串口列表
}


功能:初始化所有成员变量,建立信号槽连接,启动定时刷新。

2. 属性读取函数
portList()
 

QStringList SerialPortManager::portList() const
{return m_portList;  // 返回当前串口列表
}


isConnected()
 

bool SerialPortManager::isConnected() const
{return m_serialPort->isOpen();  // 返回串口打开状态
}


receivedData()
 

QString SerialPortManager::receivedData() const
{return m_receivedData;  // 返回接收到的数据
}


statusMessage()
 

QString SerialPortManager::statusMessage() const
{return m_statusMessage;  // 返回状态消息
}


3. 核心功能函数
refreshPorts() - 刷新串口列表
 

void SerialPortManager::refreshPorts()
{QStringList newPortList;const auto infos = QSerialPortInfo::availablePorts();  // 获取系统可用串口for (const QSerialPortInfo &info : infos) {newPortList << info.portName();  // 提取端口名}// 只有列表发生变化时才更新并发射信号if (newPortList != m_portList) {m_portList = newPortList;emit portListChanged();  // 通知QML更新}
}


connectSerialPort() - 连接串口
 

bool SerialPortManager::connectSerialPort(const QString &portName, int baudRate, int dataBits,int parity, int stopBits, int flowControl)
{if (m_serialPort->isOpen()) {m_serialPort->close();  // 如果已连接,先关闭}// 设置串口参数m_serialPort->setPortName(portName);m_serialPort->setBaudRate(static_cast<QSerialPort::BaudRate>(baudRate));m_serialPort->setDataBits(static_cast<QSerialPort::DataBits>(dataBits));m_serialPort->setParity(static_cast<QSerialPort::Parity>(parity));m_serialPort->setStopBits(static_cast<QSerialPort::StopBits>(stopBits));m_serialPort->setFlowControl(static_cast<QSerialPort::FlowControl>(flowControl));// 尝试以读写模式打开串口if (m_serialPort->open(QIODevice::ReadWrite)) {m_statusMessage = QString("已连接到 %1").arg(portName);emit statusMessageChanged();emit connectionChanged();return true;} else {m_statusMessage = QString("连接失败: %1").arg(m_serialPort->errorString());emit statusMessageChanged();return false;}
}


disconnectSerialPort() - 断开连接
 

void SerialPortManager::disconnectSerialPort()
{if (m_serialPort->isOpen()) {m_serialPort->close();  // 关闭串口m_statusMessage = "串口已断开";emit statusMessageChanged();emit connectionChanged();  // 通知连接状态改变}
}


sendData() - 发送数据
 

void SerialPortManager::sendData(const QString &data, bool hexMode)
{if (!m_serialPort->isOpen()) {m_statusMessage = "串口未连接";emit statusMessageChanged();return;}QByteArray sendArray;if (hexMode) {// 十六进制发送模式QString cleanData = data.trimmed();cleanData.remove(' ');  // 移除空格// 验证十六进制数据长度if (cleanData.length() % 2 != 0) {m_statusMessage = "十六进制数据长度必须为偶数";emit statusMessageChanged();return;}// 逐字节转换十六进制字符串为字节数据for (int i = 0; i < cleanData.length(); i += 2) {bool ok;QString byteStr = cleanData.mid(i, 2);char byte = static_cast<char>(byteStr.toInt(&ok, 16));if (ok) {sendArray.append(byte);} else {m_statusMessage = "十六进制数据格式错误";emit statusMessageChanged();return;}}} else {// 文本发送模式sendArray = data.toUtf8();  // 转换为UTF-8编码}// 发送数据并检查结果qint64 bytesWritten = m_serialPort->write(sendArray);if (bytesWritten == -1) {m_statusMessage = "发送失败";emit statusMessageChanged();} else {m_statusMessage = QString("已发送 %1 字节").arg(bytesWritten);emit statusMessageChanged();}
}


clearReceivedData() - 清空接收数据
 

void SerialPortManager::clearReceivedData()
{m_receivedData.clear();      // 清空数据缓冲区emit receivedDataChanged();  // 通知QML更新显示
}


4. 槽函数
onReadyRead() - 数据接收处理
 

void SerialPortManager::onReadyRead()
{QByteArray data = m_serialPort->readAll();  // 读取所有可用数据// 问题:这里硬编码为false,应该从QML获取设置bool hexDisplay = false;if (hexDisplay) {// 十六进制显示模式QString hexString;for (char byte : data) {// 每个字节格式化为两位十六进制数,用空格分隔hexString += QString("%1 ").arg(static_cast<quint8>(byte), 2, 16, QLatin1Char('0')).toUpper();}m_receivedData += hexString;} else {// 文本显示模式QString text;for (char byte : data) {if (byte >= 32 && byte <= 126) {// 可打印字符直接显示text += QChar(byte);} else if (byte == '\r' || byte == '\n' || byte == '\t') {// 特殊控制字符显示text += QChar(byte);} else {// 不可见字符显示为十六进制格式text += QString("[%1]").arg(static_cast<quint8>(byte), 2, 16, QLatin1Char('0')).toUpper();}}m_receivedData += text;}emit receivedDataChanged();  // 通知数据更新
}


onErrorOccurred() - 错误处理
 

void SerialPortManager::onErrorOccurred(QSerialPort::SerialPortError error)
{if (error != QSerialPort::NoError) {// 生成错误信息m_statusMessage = QString("串口错误: %1").arg(m_serialPort->errorString());emit statusMessageChanged();// 如果串口处于打开状态,关闭它if (m_serialPort->isOpen()) {m_serialPort->close();emit connectionChanged();  // 通知连接状态改变}}
}


5. 主函数 main()
 

int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);// 注册C++类到QML系统qmlRegisterType<SerialPortManager>("SerialPort", 1, 0, "SerialPortManager");QQmlApplicationEngine engine;engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;return app.exec();
}

二、所有源码

SerialPortManager.h文件源码

#ifndef SERIALPORTMANAGER_H
#define SERIALPORTMANAGER_H#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTimer>
#include <QDebug>class SerialPortManager : public QObject
{Q_OBJECTQ_PROPERTY(QStringList portList READ portList NOTIFY portListChanged)Q_PROPERTY(bool isConnected READ isConnected NOTIFY connectionChanged)Q_PROPERTY(QString receivedData READ receivedData NOTIFY receivedDataChanged)Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY statusMessageChanged)public:explicit SerialPortManager(QObject *parent = nullptr);QStringList portList() const;bool isConnected() const;QString receivedData() const;QString statusMessage() const;Q_INVOKABLE void refreshPorts();Q_INVOKABLE bool connectSerialPort(const QString &portName, int baudRate, int dataBits,int parity, int stopBits, int flowControl);Q_INVOKABLE void disconnectSerialPort();Q_INVOKABLE void sendData(const QString &data, bool hexMode);Q_INVOKABLE void clearReceivedData();signals:  // 添加signals关键字void portListChanged();void connectionChanged();void receivedDataChanged();void statusMessageChanged();private slots:void onReadyRead();void onErrorOccurred(QSerialPort::SerialPortError error);private:QSerialPort *m_serialPort;QStringList m_portList;QString m_receivedData;QString m_statusMessage;bool m_hexDisplay;QTimer *m_portRefreshTimer;
};#endif // SERIALPORTMANAGER_H

SerialPortManager.cpp文件源码

#include "SerialPortManager.h"SerialPortManager::SerialPortManager(QObject *parent): QObject(parent), m_serialPort(new QSerialPort(this)), m_hexDisplay(false)
{// 初始化定时器,定时刷新串口列表m_portRefreshTimer = new QTimer(this);connect(m_portRefreshTimer, &QTimer::timeout, this, &SerialPortManager::refreshPorts);m_portRefreshTimer->start(2000); // 每2秒刷新一次// 连接串口信号connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortManager::onReadyRead);connect(m_serialPort, &QSerialPort::errorOccurred, this, &SerialPortManager::onErrorOccurred);refreshPorts();
}QStringList SerialPortManager::portList() const
{return m_portList;
}bool SerialPortManager::isConnected() const
{return m_serialPort->isOpen();
}QString SerialPortManager::receivedData() const
{return m_receivedData;
}QString SerialPortManager::statusMessage() const
{return m_statusMessage;
}void SerialPortManager::refreshPorts()
{QStringList newPortList;const auto infos = QSerialPortInfo::availablePorts();for (const QSerialPortInfo &info : infos) {newPortList << info.portName();}if (newPortList != m_portList) {m_portList = newPortList;emit portListChanged();}
}bool SerialPortManager::connectSerialPort(const QString &portName, int baudRate, int dataBits,int parity, int stopBits, int flowControl)
{if (m_serialPort->isOpen()) {m_serialPort->close();}m_serialPort->setPortName(portName);m_serialPort->setBaudRate(static_cast<QSerialPort::BaudRate>(baudRate));m_serialPort->setDataBits(static_cast<QSerialPort::DataBits>(dataBits));m_serialPort->setParity(static_cast<QSerialPort::Parity>(parity));m_serialPort->setStopBits(static_cast<QSerialPort::StopBits>(stopBits));m_serialPort->setFlowControl(static_cast<QSerialPort::FlowControl>(flowControl));if (m_serialPort->open(QIODevice::ReadWrite)) {m_statusMessage = QString("已连接到 %1").arg(portName);emit statusMessageChanged();emit connectionChanged();return true;} else {m_statusMessage = QString("连接失败: %1").arg(m_serialPort->errorString());emit statusMessageChanged();return false;}
}void SerialPortManager::disconnectSerialPort()
{if (m_serialPort->isOpen()) {m_serialPort->close();m_statusMessage = "串口已断开";emit statusMessageChanged();emit connectionChanged();}
}void SerialPortManager::sendData(const QString &data, bool hexMode)
{if (!m_serialPort->isOpen()) {m_statusMessage = "串口未连接";emit statusMessageChanged();return;}QByteArray sendArray;if (hexMode) {// 十六进制发送QString cleanData = data.trimmed();cleanData.remove(' ');if (cleanData.length() % 2 != 0) {m_statusMessage = "十六进制数据长度必须为偶数";emit statusMessageChanged();return;}for (int i = 0; i < cleanData.length(); i += 2) {bool ok;QString byteStr = cleanData.mid(i, 2);char byte = static_cast<char>(byteStr.toInt(&ok, 16));if (ok) {sendArray.append(byte);} else {m_statusMessage = "十六进制数据格式错误";emit statusMessageChanged();return;}}} else {// 文本发送sendArray = data.toUtf8();}qint64 bytesWritten = m_serialPort->write(sendArray);if (bytesWritten == -1) {m_statusMessage = "发送失败";emit statusMessageChanged();} else {m_statusMessage = QString("已发送 %1 字节").arg(bytesWritten);emit statusMessageChanged();}
}void SerialPortManager::clearReceivedData()
{m_receivedData.clear();emit receivedDataChanged();
}void SerialPortManager::onReadyRead()
{QByteArray data = m_serialPort->readAll();// 临时设置十六进制显示,实际应该从QML传递这个设置bool hexDisplay = false; // 这里需要从QML获取设置if (hexDisplay) {// 十六进制显示QString hexString;for (char byte : data) {hexString += QString("%1 ").arg(static_cast<quint8>(byte), 2, 16, QLatin1Char('0')).toUpper();}m_receivedData += hexString;} else {// 文本显示// 过滤不可见字符,保留可打印字符QString text;for (char byte : data) {if (byte >= 32 && byte <= 126) {text += QChar(byte);} else if (byte == '\r' || byte == '\n' || byte == '\t') {text += QChar(byte);} else {text += QString("[%1]").arg(static_cast<quint8>(byte), 2, 16, QLatin1Char('0')).toUpper();}}m_receivedData += text;}emit receivedDataChanged();
}void SerialPortManager::onErrorOccurred(QSerialPort::SerialPortError error)
{if (error != QSerialPort::NoError) {m_statusMessage = QString("串口错误: %1").arg(m_serialPort->errorString());emit statusMessageChanged();if (m_serialPort->isOpen()) {m_serialPort->close();emit connectionChanged();}}
}

main.cpp文件源码

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "SerialPortManager.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);// 注册C++类到QMLqmlRegisterType<SerialPortManager>("SerialPort", 1, 0, "SerialPortManager");QQmlApplicationEngine engine;engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;return app.exec();
}

main.qml文件源码

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import SerialPort 1.0ApplicationWindow {id: windowwidth: 1000height: 800title: "串口调试工具"visible: true// 定义颜色主题property color primaryColor: "#3498db"      // 主色调 - 蓝色property color secondaryColor: "#2ecc71"    // 次要色调 - 绿色property color accentColor: "#e74c3c"       // 强调色 - 红色property color backgroundColor: "#f8f9fa"   // 背景色property color cardColor: "#ffffff"         // 卡片背景色property color textColor: "#2c3e50"         // 主要文字颜色property color subTextColor: "#7f8c8d"      // 次要文字颜色property color borderColor: "#bdc3c7"       // 边框颜色property color successColor: "#27ae60"      // 成功颜色property color warningColor: "#f39c12"      // 警告颜色property color errorColor: "#e74c3c"        // 错误颜色// 设置窗口背景Rectangle {anchors.fill: parentcolor: backgroundColor}// 串口管理器SerialPortManager {id: serialPort}ColumnLayout {anchors.fill: parentanchors.margins: 15spacing: 12// 串口配置区域GroupBox {title: "📡 串口配置"Layout.fillWidth: truebackground: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 8}label: Label {text: parent.titlecolor: primaryColorfont.bold: truefont.pixelSize: 14padding: 5}GridLayout {columns: 6width: parent.widthrowSpacing: 8columnSpacing: 8// 第一行Label {text: "端口:"color: textColorfont.bold: true}ComboBox {id: portComboBoxLayout.fillWidth: truemodel: serialPort.portListbackground: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}onModelChanged: {if (model.length > 0 && currentIndex === -1) {currentIndex = 0}}}Label {text: "波特率:"color: textColorfont.bold: true}ComboBox {id: baudRateComboBoxLayout.fillWidth: truemodel: ["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]currentIndex: 3 // 9600background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}}Label {text: "数据位:"color: textColorfont.bold: true}ComboBox {id: dataBitsComboBoxLayout.fillWidth: truemodel: ["5", "6", "7", "8"]currentIndex: 3 // 8background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}}// 第二行Label {text: "校验位:"color: textColorfont.bold: true}ComboBox {id: parityComboBoxLayout.fillWidth: truemodel: ["无", "奇校验", "偶校验", "标记", "空格"]background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}property var parityValues: [0, 3, 2, 1, 4]}Label {text: "停止位:"color: textColorfont.bold: true}ComboBox {id: stopBitsComboBoxLayout.fillWidth: truemodel: ["1", "1.5", "2"]background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}property var stopBitsValues: [1, 3, 2]}Label {text: "流控制:"color: textColorfont.bold: true}ComboBox {id: flowControlComboBoxLayout.fillWidth: truemodel: ["无", "硬件", "软件"]background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}property var flowControlValues: [0, 1, 2]}// 第三行 - 按钮Button {id: connectButtontext: serialPort.isConnected ? "🔌 断开连接" : "🔗 连接"Layout.columnSpan: 2Layout.fillWidth: truebackground: Rectangle {color: serialPort.isConnected ? accentColor : secondaryColorradius: 6}contentItem: Text {text: connectButton.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}onClicked: {if (serialPort.isConnected) {serialPort.disconnectSerialPort()} else {if (portComboBox.currentText) {serialPort.connectSerialPort(portComboBox.currentText,parseInt(baudRateComboBox.currentText),parseInt(dataBitsComboBox.currentText),parityComboBox.parityValues[parityComboBox.currentIndex],stopBitsComboBox.stopBitsValues[stopBitsComboBox.currentIndex],flowControlComboBox.flowControlValues[flowControlComboBox.currentIndex])}}}}Button {text: "🔄 刷新端口"Layout.fillWidth: truebackground: Rectangle {color: primaryColorradius: 6}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}onClicked: serialPort.refreshPorts()}}}// 发送区域GroupBox {title: "📤 发送数据"Layout.fillWidth: trueLayout.preferredHeight: 180background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 8}label: Label {text: parent.titlecolor: primaryColorfont.bold: truefont.pixelSize: 14padding: 5}ColumnLayout {width: parent.widthspacing: 8RowLayout {CheckBox {id: sendHexCheckBoxtext: "十六进制发送"Layout.alignment: Qt.AlignLeft}CheckBox {id: receiveHexCheckBoxtext: "十六进制显示"Layout.alignment: Qt.AlignLeft}Item { Layout.fillWidth: true }Button {text: "🧹 清空接收"background: Rectangle {color: warningColorradius: 6}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}onClicked: serialPort.clearReceivedData()}}ScrollView {Layout.fillWidth: trueLayout.preferredHeight: 80background: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 4}TextArea {id: sendTextAreaplaceholderText: "请输入要发送的数据..."placeholderTextColor: subTextColorwrapMode: TextEdit.Wrapcolor: textColorbackground: Rectangle {color: "transparent"}}}}}RowLayout {Button {text: "🚀 发送"background: Rectangle {color: secondaryColorradius: 6}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}onClicked: {serialPort.sendData(sendTextArea.text, sendHexCheckBox.checked)}}Button {text: "🗑️ 清空发送"background: Rectangle {color: subTextColorradius: 6}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}onClicked: sendTextArea.clear()}Item { Layout.fillWidth: true }Label {text: "状态: " + serialPort.statusMessagecolor: {if (serialPort.isConnected) return successColorelse if (serialPort.statusMessage.includes("错误") ||serialPort.statusMessage.includes("失败")) return errorColorelse return textColor}font.bold: truepadding: 8background: Rectangle {color: backgroundColorradius: 4border.color: borderColorborder.width: 1}}}// 接收区域GroupBox {title: "📥 接收数据"Layout.fillWidth: trueLayout.fillHeight: truebackground: Rectangle {color: cardColorborder.color: borderColorborder.width: 1radius: 8}label: Label {text: parent.titlecolor: primaryColorfont.bold: truefont.pixelSize: 14padding: 5}ColumnLayout {width: parent.widthheight: parent.heightspacing: 8ScrollView {Layout.fillWidth: trueLayout.fillHeight: truebackground: Rectangle {color: "#2c3e50"border.color: borderColorborder.width: 1radius: 4}TextArea {id: receiveTextAreatext: serialPort.receivedDatawrapMode: TextEdit.WrapreadOnly: truefont.family: "Consolas, 'Courier New', monospace"font.pixelSize: 12selectByMouse: truecolor: "#ecf0f1"background: Rectangle {color: "transparent"}}}RowLayout {Label {text: "📊 接收字节数: " + receiveTextArea.text.lengthcolor: textColorfont.bold: truepadding: 6background: Rectangle {color: backgroundColorradius: 4border.color: borderColorborder.width: 1}}Item { Layout.fillWidth: true }CheckBox {id: autoScrollCheckBoxtext: "自动滚动"checked: truecontentItem: Text {text: autoScrollCheckBox.textcolor: textColorfont.bold: true}}Button {text: "💾 保存数据"background: Rectangle {color: primaryColorradius: 6}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}onClicked: {// 这里可以添加保存数据的功能console.log("保存数据功能待实现")}}}}}}// 状态栏footer: ToolBar {background: Rectangle {color: primaryColor}RowLayout {anchors.fill: parentLabel {text: "🔧 串口调试工具 v1.0 | 就绪"color: "white"font.bold: truepadding: 8}Item { Layout.fillWidth: true }Label {text: new Date().toLocaleString(Qt.locale(), "yyyy-MM-dd hh:mm:ss")color: "white"padding: 8}}}
}

三、效果演示

http://www.dtcms.com/a/457036.html

相关文章:

  • BMS(电池管理系统)的主要功能和架构简述
  • asp业务网站视频链接生成器
  • Flask模板中使用React、ant-design、@ant-design/icons示例模板
  • 站长源码之家网络营销中常用的营销策略
  • JAVA算法练习题day35
  • 德州做网站施工企业准则
  • 深圳网站建设十强河北省城乡住房和城乡建设厅网站
  • 数字增量式编码器:工业自动化的“精密神经元”
  • Spring AI-流式编程
  • 手写 Promise.all 的原理与实现
  • 关于windows系统事件查看器的初步理解
  • Linux 线程概念与虚拟地址空间深度解析
  • 一套智慧工地云平台源码,支持监管端、项目管理端,Java+Spring Cloud +UniApp +MySql技术开发
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P05-05 游戏效果委托
  • 音频audio播放两种方式:MediaPlayer和AudioTrack对比
  • K8s学习笔记(十五) pause容器与init容器
  • DVWA靶场之十六:未验证的重定向漏洞(Open HTTP Redirect)
  • 上海网站建设免费推做网站的软件 简单易学
  • 面部情绪识别数据集的介绍和下载
  • Golang中的HTTP请求凝聚器
  • 网站建设多少钱一平米中铁建设集团门户网登陆
  • Linux shell学习(更新中....)
  • 自动生成API文档与故障排查决策树的NLP应用
  • 手机怎么制作钓鱼网站建设文明网 联盟网站的
  • Rust 的类型自动解引用:隐藏在人体工学设计中的魔法
  • AVX-512深度实现分析:从原理到LLaMA.cpp的性能优化艺术
  • 前端玩转大模型,DeepSeek-R1 蒸馏 Llama 模型的 Bedrock 部署
  • 计算机网络-运输层
  • OSPF协议详解5:实验 - 计时器、度量值与其他高级配置
  • OpenCV(五):鼠标控制