分享

《Qt C 项目实战推荐》系列第十一期:Modbus通讯协议(全网最强客户端)

 青山代码yyf 2025-04-03 发布于广西
恭喜你来到《Qt C/C++项目实战推荐》系列第十一期!本期我们将聚焦于一个实用且充满挑战的项目主题——【Modbus通讯协议(全网最强客户端)。无论你是刚掌握Qt C/C++基础的新手,还是希望想深入探索C/C++开发领域的开发者,这个项目都将为你打开一扇深度学习金钥匙的大门。大家可以根据所提供的技术点应用于企业开发岗位。

一:【工程项目】运行效果

图片
图片

二:【工程项目】整体结构

图片

三:【工程项目】源码分析

1:main.cpp文件
#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();  // 启动事件处理循环,返回值作为程序退出码}
2:mainwindow.h文件
#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
3:mainwindow.cpp文件
#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(101, 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(), -116), 5000);        } else {            statusBar()->showMessage(tr('Error: %1 (Code: 0x%2)')                .arg(reply->errorString())                .arg(reply->error(), -116), 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(), -116), 5000);                } else if (reply->error() != QModbusDevice::NoError) {                    statusBar()->showMessage(tr('Error: %1 (Code: 0x%2)')                        .arg(reply->errorString())                        .arg(reply->error(), -116), 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);}
4:writeregistermodel.h文件
#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();  // 通知视图刷新显示}
6:settingsdialog.h文件
#ifndef SETTINGSDIALOG_H#define SETTINGSDIALOG_H
#include <QDialog>         // Qt对话框基类#include <QSerialPort>     // Qt串口通信支持
QT_BEGIN_NAMESPACEnamespace 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
7:settingsdialog.cpp文件
#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;  // 返回配置结构体副本(保证数据封装性)}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多