恭喜你来到《Qt C/C++项目实战推荐》系列第十一期!本期我们将聚焦于一个实用且充满挑战的项目主题——【Modbus通讯协议(全网最强客户端)】。无论你是刚掌握Qt C/C++基础的新手,还是希望想深入探索C/C++开发领域的开发者,这个项目都将为你打开一扇深度学习金钥匙的大门。大家可以根据所提供的技术点应用于企业开发岗位。一:【工程项目】运行效果二:【工程项目】整体结构三:【工程项目】源码分析#include 'mainwindow.h' // 包含自定义主窗口头文件 #include <QApplication> // Qt应用程序框架核心头文件 #include <QLoggingCategory> // Qt日志分类管理头文件
int main(int argc, char *argv[]) { // TODO 发布前取消此注释(当前调试阶段需保留) // 设置modbus模块的日志过滤规则(显示所有日志) QLoggingCategory::setFilterRules(QStringLiteral('qt.modbus* = true'));
// 初始化Qt应用程序对象(必须第一个创建) QApplication a(argc, argv);
// 创建并显示主窗口 MainWindow w; // 实例化主窗口对象 w.show(); // 显示主窗口(默认隐藏状态需要手动显示)
// 进入Qt事件循环,程序持续运行直到窗口关闭 return a.exec(); // 启动事件处理循环,返回值作为程序退出码 }
#ifndef MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow> #include <QModbusDataUnit> // 包含Modbus数据单元头文件,用于数据读写操作
QT_BEGIN_NAMESPACE // 前置声明Qt类(减少头文件依赖) class QModbusClient; // Modbus客户端类,用于建立设备连接 class QModbusReply; // Modbus请求响应类,管理异步操作
namespace Ui { class MainWindow; // 由UI编译器生成的主窗口界面类 class SettingsDialog; // 设置对话框界面类 } QT_END_NAMESPACE
// 前置声明自定义类 class SettingsDialog; // 参数设置对话框业务逻辑类 class WriteRegisterModel; // 寄存器写入数据模型类
/** * @brief Modbus主控制窗口类 * * 实现Modbus客户端的主要功能,包括: * - 设备连接/断开管理 * - 寄存器数据读取/写入操作 * - 通信参数配置 * - 数据展示与交互 */ class MainWindow : public QMainWindow { Q_OBJECT // Qt元对象系统宏,支持信号槽机制
public: explicit MainWindow(QWidget *parent = nullptr); // 带父对象指针的构造函数 ~MainWindow(); // 析构函数(资源清理)
private: // 初始化界面动作关联 void initActions(); // 构造读取请求数据单元(根据界面输入生成) QModbusDataUnit readRequest() const; // 构造写入请求数据单元(根据界面输入生成) QModbusDataUnit writeRequest() const;
private slots: // 槽函数(事件响应) // UI元素事件处理 void on_connectButton_clicked(); // 连接/断开按钮点击 void onStateChanged(int state); // Modbus设备状态变化处理
// 数据操作处理 void on_readButton_clicked(); // 读取按钮点击 void readReady(); // 读取操作完成处理
void on_writeButton_clicked(); // 写入按钮点击 void on_readWriteButton_clicked(); // 读写复合操作按钮点击
// 界面状态更新 void on_connectType_currentIndexChanged(int); // 连接类型切换(如TCP/RTU) void on_writeTable_currentIndexChanged(int); // 写入表格类型切换(如保持寄存器)
private: // 成员变量 Ui::MainWindow *ui; // 主窗口界面对象指针 QModbusReply *lastRequest; // 最后发出的请求对象(用于异步操作管理) QModbusClient *modbusDevice; // Modbus客户端设备连接对象 SettingsDialog *m_settingsDialog; // 参数设置对话框指针 WriteRegisterModel *writeModel; // 寄存器写入数据模型(数据格式化与校验) };
#endif // MAINWINDOW_H
#include 'mainwindow.h' #include 'ui_mainwindow.h' #include 'settingsdialog.h' #include 'writeregistermodel.h'
#include <QModbusTcpClient> // Modbus TCP客户端实现 #include <QModbusRtuSerialMaster> // Modbus RTU串口主站实现 #include <QStandardItemModel> // 标准数据模型(用于下拉框数据) #include <QStatusBar> // 状态栏组件 #include <QUrl> // URL解析处理
// Modbus连接类型枚举(串口/TCP) enum ModbusConnection { Serial, Tcp };
/* 主窗口构造函数 */ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , lastRequest(nullptr) , modbusDevice(nullptr) { ui->setupUi(this); // 初始化UI界面
m_settingsDialog = new SettingsDialog(this); // 创建参数设置对话框
initActions(); // 初始化菜单栏动作关联
// 初始化写入寄存器数据模型 writeModel = new WriteRegisterModel(this); writeModel->setStartAddress(ui->writeAddress->value()); // 设置起始地址 writeModel->setNumberOfValues(ui->writeSize->currentText()); // 设置数据长度
// 绑定模型到表格视图,隐藏第三列(地址列) ui->writeValueTable->setModel(writeModel); ui->writeValueTable->hideColumn(2); connect(writeModel, &WriteRegisterModel::updateViewport, ui->writeValueTable->viewport(), static_cast<void (QWidget::*)()>(&QWidget::update));
// 初始化写入寄存器类型选择框(4种Modbus寄存器类型) ui->writeTable->addItem(tr('Coils'), QModbusDataUnit::Coils); ui->writeTable->addItem(tr('Discrete Inputs'), QModbusDataUnit::DiscreteInputs); ui->writeTable->addItem(tr('Input Registers'), QModbusDataUnit::InputRegisters); ui->writeTable->addItem(tr('Holding Registers'), QModbusDataUnit::HoldingRegisters);
// 初始化连接类型(默认串口) ui->connectType->setCurrentIndex(0); on_connectType_currentIndexChanged(0); // 手动触发连接类型变更
// 初始化写入长度选择模型(1-10个寄存器) auto model = new QStandardItemModel(10, 1, this); for (int i = 0; i < 10; ++i) model->setItem(i, new QStandardItem(QStringLiteral('%1').arg(i + 1))); ui->writeSize->setModel(model); ui->writeSize->setCurrentText('10'); connect(ui->writeSize,&QComboBox::currentTextChanged, writeModel, &WriteRegisterModel::setNumberOfValues);
// 地址变更联动处理:根据地址值动态启用/禁用长度选项 auto valueChanged = static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged); connect(ui->writeAddress, valueChanged, writeModel, &WriteRegisterModel::setStartAddress); connect(ui->writeAddress, valueChanged, this, [this, model](int i) { // 计算可用长度范围(地址+长度不超过10) int lastPossibleIndex = 0; const int currentIndex = ui->writeSize->currentIndex(); for (int ii = 0; ii < 10; ++ii) { if (ii < (10 - i)) { lastPossibleIndex = ii; model->item(ii)->setEnabled(true); } else { model->item(ii)->setEnabled(false); } } if (currentIndex > lastPossibleIndex) ui->writeSize->setCurrentIndex(lastPossibleIndex); }); }
/* 析构函数:清理资源 */ MainWindow::~MainWindow() { if (modbusDevice) { modbusDevice->disconnectDevice(); // 断开设备连接 delete modbusDevice; // 释放设备对象 } delete ui; // 删除UI对象 }
/* 初始化菜单栏动作状态 */ void MainWindow::initActions() { // 初始按钮状态设置 ui->actionConnect->setEnabled(true); ui->actionDisconnect->setEnabled(false); ui->actionExit->setEnabled(true); ui->actionOptions->setEnabled(true);
// 菜单项信号连接 connect(ui->actionConnect, &QAction::triggered, this, &MainWindow::on_connectButton_clicked); connect(ui->actionDisconnect, &QAction::triggered, this, &MainWindow::on_connectButton_clicked); connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close); connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show); }
/* 连接类型切换处理 */ void MainWindow::on_connectType_currentIndexChanged(int index) { // 清理旧的连接设备 if (modbusDevice) { modbusDevice->disconnectDevice(); delete modbusDevice; modbusDevice = nullptr; }
// 创建新的连接设备对象 auto type = static_cast<ModbusConnection> (index); if (type == Serial) { modbusDevice = new QModbusRtuSerialMaster(this); // 串口主站 } else if (type == Tcp) { modbusDevice = new QModbusTcpClient(this); // TCP客户端 if (ui->portEdit->text().isEmpty()) ui->portEdit->setText(QLatin1Literal('127.0.0.1:502')); // 默认TCP地址 }
// 错误处理连接 connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) { statusBar()->showMessage(modbusDevice->errorString(), 5000); });
// 设备创建校验 if (!modbusDevice) { ui->connectButton->setDisabled(true); statusBar()->showMessage(tr('Create device failed'), 5000); } else { connect(modbusDevice, &QModbusClient::stateChanged, this, &MainWindow::onStateChanged); // 状态变化监控 } }
/* 连接按钮点击处理 */ void MainWindow::on_connectButton_clicked() { if (!modbusDevice) return;
statusBar()->clearMessage(); if (modbusDevice->state() != QModbusDevice::ConnectedState) { // 连接操作 // 配置连接参数 if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) { // 串口参数设置 modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, m_settingsDialog->settings().parity); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_settingsDialog->settings().baud); modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_settingsDialog->settings().dataBits); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_settingsDialog->settings().stopBits); } else { // TCP参数设置 const QUrl url = QUrl::fromUserInput(ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host()); } // 超时和重试设置 modbusDevice->setTimeout(m_settingsDialog->settings().responseTime); modbusDevice->setNumberOfRetries(m_settingsDialog->settings().numberOfRetries);
// 执行连接 if (!modbusDevice->connectDevice()) { statusBar()->showMessage(tr('Connect failed: ') + modbusDevice->errorString(), 5000); } else { ui->actionConnect->setEnabled(false); ui->actionDisconnect->setEnabled(true); } } else { // 断开操作 modbusDevice->disconnectDevice(); ui->actionConnect->setEnabled(true); ui->actionDisconnect->setEnabled(false); } }
/* 设备状态变化处理 */ void MainWindow::onStateChanged(int state) { bool connected = (state != QModbusDevice::UnconnectedState); ui->actionConnect->setEnabled(!connected); ui->actionDisconnect->setEnabled(connected);
// 更新按钮文本 if (state == QModbusDevice::UnconnectedState) ui->connectButton->setText(tr('Connect')); else if (state == QModbusDevice::ConnectedState) ui->connectButton->setText(tr('Disconnect')); }
/* 读取按钮点击处理 */ void MainWindow::on_readButton_clicked() { if (!modbusDevice) return; ui->readValue->clear(); // 清空读取结果 statusBar()->clearMessage();
// 发送读取请求 if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) { if (!reply->isFinished()) // 异步处理完成信号 connect(reply, &QModbusReply::finished, this, &MainWindow::readReady); else delete reply; // 广播请求立即完成需手动删除 } else { statusBar()->showMessage(tr('Read error: ') + modbusDevice->errorString(), 5000); } }
/* 读取完成处理 */ void MainWindow::readReady() { auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return;
if (reply->error() == QModbusDevice::NoError) { // 成功处理 const QModbusDataUnit unit = reply->result(); for (uint i = 0; i < unit.valueCount(); i++) { // 格式化显示数据(线圈用十进制,寄存器用十六进制) const QString entry = tr('Address: %1, Value: %2') .arg(unit.startAddress() + i) .arg(QString::number(unit.value(i), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16)); ui->readValue->addItem(entry); } } else { // 错误处理 if (reply->error() == QModbusDevice::ProtocolError) { statusBar()->showMessage(tr('Protocol Error: %1 (Exception: 0x%2)') .arg(reply->errorString()) .arg(reply->rawResult().exceptionCode(), -1, 16), 5000); } else { statusBar()->showMessage(tr('Error: %1 (Code: 0x%2)') .arg(reply->errorString()) .arg(reply->error(), -1, 16), 5000); } } reply->deleteLater(); // 安全删除回复对象 }
/* 写入按钮点击处理 */ void MainWindow::on_writeButton_clicked() { if (!modbusDevice) return; statusBar()->clearMessage();
QModbusDataUnit writeUnit = writeRequest(); // 构造写入数据单元 QModbusDataUnit::RegisterType table = writeUnit.registerType();
// 从数据模型获取值 for (uint i = 0; i < writeUnit.valueCount(); i++) { if (table == QModbusDataUnit::Coils) writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]); else writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]); }
// 发送写入请求 if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, ui->serverEdit->value())) { if (!reply->isFinished()) { // 异步处理错误响应 connect(reply, &QModbusReply::finished, this, [this, reply]() { if (reply->error() == QModbusDevice::ProtocolError) { statusBar()->showMessage(tr('Write Error: %1 (Exception: 0x%2)') .arg(reply->errorString()) .arg(reply->rawResult().exceptionCode(), -1, 16), 5000); } else if (reply->error() != QModbusDevice::NoError) { statusBar()->showMessage(tr('Error: %1 (Code: 0x%2)') .arg(reply->errorString()) .arg(reply->error(), -1, 16), 5000); } reply->deleteLater(); }); } else { reply->deleteLater(); // 立即完成的广播请求 } } else { statusBar()->showMessage(tr('Write error: ') + modbusDevice->errorString(), 5000); } }
/* 读写复合操作处理 */ void MainWindow::on_readWriteButton_clicked() { if (!modbusDevice) return; ui->readValue->clear(); statusBar()->clearMessage();
// 准备写入数据(类似写入操作) QModbusDataUnit writeUnit = writeRequest(); QModbusDataUnit::RegisterType table = writeUnit.registerType(); for (uint i = 0; i < writeUnit.valueCount(); i++) { if (table == QModbusDataUnit::Coils) writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]); else writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]); }
// 发送读写复合请求 if (auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeUnit, ui->serverEdit->value())) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &MainWindow::readReady); else delete reply; } else { statusBar()->showMessage(tr('Read error: ') + modbusDevice->errorString(), 5000); } }
/* 写入寄存器类型变更处理 */ void MainWindow::on_writeTable_currentIndexChanged(int index) { // 根据寄存器类型显示不同列 const bool coilsOrHolding = index == 0 || index == 3; ui->writeValueTable->setColumnHidden(1, index != 0); // 线圈显示布尔列 ui->writeValueTable->setColumnHidden(2, index != 3); // 保持寄存器显示数值列
// 控件状态更新 ui->readWriteButton->setEnabled(index == 3); // 仅保持寄存器允许读写复合 ui->writeButton->setEnabled(coilsOrHolding); // 仅线圈和保持寄存器可写 ui->writeGroupBox->setEnabled(coilsOrHolding); // 禁用不可写的寄存器类型 }
/* 构造读取请求数据单元 */ QModbusDataUnit MainWindow::readRequest() const { const auto table = static_cast<QModbusDataUnit::RegisterType> (ui->writeTable->currentData().toInt()); int startAddress = ui->readAddress->value(); int numberOfEntries = qMin(ui->readSize->currentText().toInt(), 10 - startAddress); // 最大10个寄存器 return QModbusDataUnit(table, startAddress, numberOfEntries); }
/* 构造写入请求数据单元 */ QModbusDataUnit MainWindow::writeRequest() const { const auto table = static_cast<QModbusDataUnit::RegisterType> (ui->writeTable->currentData().toInt()); int startAddress = ui->writeAddress->value(); int numberOfEntries = qMin(ui->writeSize->currentText().toInt(), 10 - startAddress); // 地址范围保护 return QModbusDataUnit(table, startAddress, numberOfEntries); }
#ifndef WRITEREGISTERMODEL_H #define WRITEREGISTERMODEL_H
#include <QAbstractItemModel> // Qt项模型基类 #include <QBitArray> // 位数组容器(用于存储线圈状态) #include <QObject> // Qt对象基类
/** * @brief Modbus寄存器写入数据模型类 * * 继承自QAbstractTableModel,为QTableView提供数据支持,管理两类寄存器数据: * - 线圈(Coils):使用QBitArray存储布尔值 * - 保持寄存器(Holding Registers):使用16位无符号整型数组 * * 支持动态调整寄存器起始地址和数据长度,提供数据编辑接口 */ class WriteRegisterModel : public QAbstractTableModel { Q_OBJECT // 启用Qt元对象系统(信号槽机制)
public: explicit WriteRegisterModel(QObject *parent = nullptr); ///< 构造函数,初始化数据存储
// 必须实现的表格模型接口 int rowCount(const QModelIndex &parent = QModelIndex()) const override; ///< 返回当前数据行数(由m_number决定) int columnCount(const QModelIndex &parent = QModelIndex()) const override; ///< 列数固定为3(地址、线圈状态、寄存器值)
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; ///< 获取指定单元格数据(显示/编辑角色) QVariant headerData(int section, Qt::Orientation orientation, int role) const override; ///< 设置表头显示内容 bool setData(const QModelIndex &index, const QVariant &value, int role) override; ///< 修改指定单元格数据并触发视图更新
Qt::ItemFlags flags(const QModelIndex &index) const override; ///< 设置单元格属性(可编辑性等)
public slots: void setStartAddress(int address); ///< 设置起始地址(影响数据显示的地址列) void setNumberOfValues(const QString &number); ///< 设置数据条目数(动态调整存储空间大小)
signals: void updateViewport(); ///< 数据变化时通知视图更新显示区域
public: // 公开成员变量(需谨慎操作) int m_number; ///< 当前管理的寄存器数量 int m_address; ///< 起始地址(显示用,不参与实际通信) QBitArray m_coils; ///< 线圈状态存储(位数组,1位表示1个线圈) QVector<quint16> m_holdingRegisters; ///< 保持寄存器数值存储(16位无符号整型)
};
#endif // WRITEREGISTERMODEL_H 5:writeregistermodel.cpp文件#include 'writeregistermodel.h'
// 定义表格列枚举常量 enum { NumColumn = 0, // 行号列 CoilsColumn = 1, // 线圈状态列 HoldingColumn = 2, // 保持寄存器值列 ColumnCount = 3, // 总列数 RowCount = 10 // 总行数(固定10行) };
/* 构造函数:初始化数据存储 */ WriteRegisterModel::WriteRegisterModel(QObject *parent) : QAbstractTableModel(parent), m_coils(RowCount, false), // 初始化线圈数组(10位,默认false) m_holdingRegisters(RowCount, 0u) // 初始化保持寄存器数组(10个元素,默认0) { }
/* 返回模型行数(固定10行) */ int WriteRegisterModel::rowCount(const QModelIndex &/*parent*/) const { return RowCount; // 固定显示10行数据 }
/* 返回模型列数(3列) */ int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const { return ColumnCount; // 行号 | 线圈 | 保持寄存器值 }
/* 获取单元格数据(核心数据接口) */ QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const { // 有效性检查 if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount) return QVariant();
// 数据一致性断言 Q_ASSERT(m_coils.count() == RowCount); Q_ASSERT(m_holdingRegisters.count() == RowCount);
// 行号列:返回当前行号(格式化为字符串) if (index.column() == NumColumn && role == Qt::DisplayRole) return QString::number(index.row());
// 线圈列:返回复选框状态(Checked/Unchecked) if (index.column() == CoilsColumn && role == Qt::CheckStateRole) return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;
// 保持寄存器列:返回16进制格式化字符串(如0x00FF) else if (index.column() == HoldingColumn && role == Qt::DisplayRole) return QString('0x%1').arg(QString::number(m_holdingRegisters.at(index.row()), 16));
return QVariant(); // 其他情况返回空值 }
/* 设置表头显示内容 */ QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant();
// 水平表头设置 if (orientation == Qt::Horizontal) { switch (section) { case NumColumn: return QStringLiteral('#'); // 行号列标题 case CoilsColumn: return QStringLiteral('Coils '); // 线圈列标题(带空格保持对齐) case HoldingColumn: return QStringLiteral('Holding Registers'); // 保持寄存器列标题 default: break; } } return QVariant(); // 垂直方向无表头 }
/* 修改单元格数据(用户编辑入口) */ bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role) { // 有效性检查 if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount) return false;
// 数据一致性断言 Q_ASSERT(m_coils.count() == RowCount); Q_ASSERT(m_holdingRegisters.count() == RowCount);
// 线圈列处理(复选框状态变更) if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { auto s = static_cast<Qt::CheckState>(value.toUInt()); s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row()); emit dataChanged(index, index); // 通知视图更新 return true; }
// 保持寄存器列处理(16进制文本输入) if (index.column() == HoldingColumn && role == Qt::EditRole) { bool result = false; quint16 newValue = value.toString().toUShort(&result, 16); // 尝试转换为16进制数 if (result) m_holdingRegisters[index.row()] = newValue; // 仅有效时更新
emit dataChanged(index, index); // 通知视图更新 return result; // 返回转换是否成功 }
return false; // 其他情况不处理 }
/* 设置单元格属性(可编辑性/可选中等) */ Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const { // 无效索引返回默认标志 if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount) return QAbstractTableModel::flags(index);
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
// 地址范围外禁用单元格(基于m_address和m_number) if ((index.row() < m_address) || (index.row() >= (m_address + m_number))) flags &= ~Qt::ItemIsEnabled; // 移除可用标志
// 线圈列:添加复选框支持 if (index.column() == CoilsColumn) return flags | Qt::ItemIsUserCheckable;
// 保持寄存器列:添加可编辑支持 if (index.column() == HoldingColumn) return flags | Qt::ItemIsEditable;
return flags; // 其他列不可编辑 }
/* 设置起始地址(影响可用行范围) */ void WriteRegisterModel::setStartAddress(int address) { m_address = address; // 更新起始地址 emit updateViewport(); // 通知视图刷新显示 }
/* 设置寄存器数量(动态调整可用行数) */ void WriteRegisterModel::setNumberOfValues(const QString &number) { m_number = number.toInt(); // 字符串转整型 emit updateViewport(); // 通知视图刷新显示 }
#ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H
#include <QDialog> // Qt对话框基类 #include <QSerialPort> // Qt串口通信支持
QT_BEGIN_NAMESPACE namespace Ui { class SettingsDialog; // 由UI编译器生成的前向声明 } QT_END_NAMESPACE
/** * @brief 串口参数配置对话框类 * * 提供用户界面用于设置串口通信参数,包含以下配置项: * - 波特率 * - 数据位 * - 停止位 * - 校验方式 * - 超时时间 * - 重试次数 */ class SettingsDialog : public QDialog { Q_OBJECT // 启用Qt元对象系统(信号槽机制)
public: /** * @brief 串口配置参数结构体 * * 存储所有可配置的串口参数,包含默认值: * - 默认偶校验(EvenParity) * - 默认波特率19200 * - 默认8位数据位 * - 默认1位停止位 * - 默认响应超时1秒 * - 默认重试次数3次 */ struct Settings { int parity = QSerialPort::EvenParity; ///< 校验方式(None/Even/Odd) int baud = QSerialPort::Baud19200; ///< 波特率(9600/19200/115200等) int dataBits = QSerialPort::Data8; ///< 数据位(5/6/7/8) int stopBits = QSerialPort::OneStop; ///< 停止位(1/1.5/2) int responseTime = 1000; ///< 响应超时(毫秒) int numberOfRetries = 3; ///< 通信失败重试次数 };
explicit SettingsDialog(QWidget *parent = nullptr); ///< 构造函数(初始化默认配置) ~SettingsDialog(); ///< 析构函数(释放UI资源)
Settings settings() const; ///< 获取当前配置参数(返回结构体副本)
private: Settings m_settings; ///< 当前配置参数存储 Ui::SettingsDialog *ui; ///< UI界面元素指针(由uic工具生成) };
#endif // SETTINGSDIALOG_H
#include 'settingsdialog.h' #include 'ui_settingsdialog.h' // 由Qt uic工具自动生成的UI头文件
/* 构造函数:初始化配置界面 */ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); // 加载并创建UI组件
// 初始化各控件显示值(基于默认配置) ui->parityCombo->setCurrentIndex(1); // 默认选择索引1(偶校验EvenParity) ui->baudCombo->setCurrentText(QString::number(m_settings.baud)); // 波特率显示19200 ui->dataBitsCombo->setCurrentText(QString::number(m_settings.dataBits)); // 数据位显示8 ui->stopBitsCombo->setCurrentText(QString::number(m_settings.stopBits)); // 停止位显示1 ui->timeoutSpinner->setValue(m_settings.responseTime); // 超时时间默认1000ms ui->retriesSpinner->setValue(m_settings.numberOfRetries); // 重试次数默认3
// 应用按钮点击事件处理 connect(ui->applyButton, &QPushButton::clicked, [this]() { // 从UI控件获取最新参数值 m_settings.parity = ui->parityCombo->currentIndex(); // 特殊处理校验位枚举映射(假设下拉项顺序为None(0), Even(1), Odd(2)) // QSerialPort::Parity枚举定义:NoParity=0, EvenParity=2, OddParity=3 if (m_settings.parity > 0) m_settings.parity++; // 将索引转换为对应的枚举值(Even从1→2,Odd从2→3)
m_settings.baud = ui->baudCombo->currentText().toInt(); // 获取波特率数值 m_settings.dataBits = ui->dataBitsCombo->currentText().toInt(); // 数据位数值 m_settings.stopBits = ui->stopBitsCombo->currentText().toInt(); // 停止位数值 m_settings.responseTime = ui->timeoutSpinner->value(); // 超时时间 m_settings.numberOfRetries = ui->retriesSpinner->value(); // 重试次数
hide(); // 应用后隐藏对话框(非关闭,保留配置) }); }
/* 析构函数:清理UI资源 */ SettingsDialog::~SettingsDialog() { delete ui; // 删除由ui->setupUi()创建的UI对象 }
/* 获取当前配置参数 */ SettingsDialog::Settings SettingsDialog::settings() const { return m_settings; // 返回配置结构体副本(保证数据封装性) }
|