国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Qt6教程之三(15) Modbus通信

這篇具有很好參考價(jià)值的文章主要介紹了Qt6教程之三(15) Modbus通信。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一 Modbus協(xié)議簡(jiǎn)介

Modbus誕生于1979年 莫迪康公司 后來(lái)被施耐德電氣公司收購(gòu)。Modbus提供通用語(yǔ)言用于彼此通信的設(shè)備和設(shè)備。
Modbus已經(jīng)成為工業(yè)領(lǐng)域通信協(xié)議的業(yè)界標(biāo)準(zhǔn),并且現(xiàn)在是工業(yè)電子設(shè)備之間常用的連接方式。Modbus作為目前工業(yè)領(lǐng)域應(yīng)用最廣泛的協(xié)議。

Modbus優(yōu)點(diǎn):

  1. Modbus協(xié)議標(biāo)準(zhǔn)開(kāi)放、公開(kāi)發(fā)表且無(wú)版權(quán)要求;
  2. Modbus協(xié)議支持多種電氣接口,包括RS232、RS485、TCP/IP等,還可以在各種介質(zhì)上傳輸,如雙絞線、光纖、紅外、無(wú)線等;
  3. Modbus協(xié)議消息幀格式簡(jiǎn)單、緊湊、通俗易懂。用戶理解和使用簡(jiǎn)單,廠商容易開(kāi)發(fā)和集成,方便形成工業(yè)控制網(wǎng)絡(luò)。

Modbus通信過(guò)程:

  1. Modbus是一主多從的通信協(xié)議: Modbus通信中只有一個(gè)設(shè)備可以發(fā)送請(qǐng)求。
  2. 主機(jī)在同一時(shí)間內(nèi)只能向一個(gè)從機(jī)發(fā)送請(qǐng)求,總線上每次只有一個(gè)數(shù)據(jù)進(jìn)行傳輸,即主機(jī)發(fā)送,從機(jī)應(yīng)答,主機(jī)不發(fā)送,總線上就沒(méi)有數(shù)據(jù)通信。
  3. 從機(jī)不會(huì)自己發(fā)送消息給主站,只能回復(fù)從主機(jī)發(fā)送的消息請(qǐng)求。
  4. Modbus并沒(méi)有忙機(jī)制判斷,需要通過(guò)軟件的方式來(lái)判斷是否從機(jī)是否正常接收。

Modbus存儲(chǔ)區(qū)讀寫(xiě)

  1. Modbus協(xié)議規(guī)定了4個(gè)存儲(chǔ)區(qū) 分別是0 1 3 4區(qū) 其中0區(qū)和4區(qū)是可讀可寫(xiě),1區(qū)和3區(qū)是只讀。
  2. 主機(jī)向從機(jī)獲取數(shù)據(jù)時(shí),只需要告訴從機(jī)數(shù)據(jù)的起始地址,還有獲取多少字節(jié)的數(shù)據(jù),實(shí)際上就是對(duì)從機(jī)設(shè)備對(duì)應(yīng)的實(shí)際存儲(chǔ)空間進(jìn)行讀寫(xiě)。

Modbus協(xié)議類型

Modbus協(xié)議類型常見(jiàn)的有幾下四種:

  1. Modbus-RTU:? 只支持串口, 使用緊湊的十六進(jìn)制表示數(shù)據(jù),后續(xù)的命令/數(shù)據(jù)帶有循環(huán)冗余校驗(yàn)的校驗(yàn)和。RTU模式比較常用。
  2. Modbus-ASCII:只支持串口, 采用Ascii碼表示數(shù)據(jù),每8Bit 字節(jié)作為兩個(gè)ASCII字符發(fā)送,采用縱向冗余校驗(yàn)的校驗(yàn)和;
  3. Modbus-TCP:支持網(wǎng)口,把MODBUS作為應(yīng)用層協(xié)議,TCP/IP作為下層協(xié)議。并且不需要從從機(jī)地址,而是需要MBAP報(bào)文頭,因?yàn)?/span>TCP本身就具有校驗(yàn)差錯(cuò)的能力故該協(xié)議不需要差錯(cuò)校驗(yàn)

  4. ModbusPlus:是一種典型的令牌環(huán)網(wǎng),完整定義了通訊協(xié)議、網(wǎng)絡(luò)結(jié)構(gòu)、連接電纜(或者光纜)以及安裝工具等方面的性能指標(biāo)。

Modbus協(xié)議的三種傳輸模式

Modbus協(xié)議是一項(xiàng)應(yīng)用層報(bào)文傳輸協(xié)議,包括ASCII、RTU、TCP三種報(bào)文類型。標(biāo)準(zhǔn)的Modbus協(xié)議物理層接口有RS232、RS422、RS485和以太網(wǎng)接口,采用master/slave方式通信。

  1. ASCII:??采用LRC校驗(yàn);
  2. RTU(遠(yuǎn)程終端控制系統(tǒng)):?采用16 位CRC校驗(yàn);
  3. TCP/IP:不使用校驗(yàn),因?yàn)門CP協(xié)議是一個(gè)面向連接的可靠協(xié)議;


Modbus的4種操作對(duì)象

Modbus的4種操作對(duì)象分別是線圈Coils、離散輸入DiscreteInputs、輸入寄存器InputRegisters、保持寄存器HoldingRegisters。四種操作對(duì)象的讀寫(xiě)權(quán)限如下:
線圈:PLC的輸出位,開(kāi)關(guān)量,在MOdbus中可讀可寫(xiě);
離散輸入:PLC的輸入位,開(kāi)關(guān)量,在Modbus中只讀;
輸入寄存器:PLC中只能從模擬量輸入端改變的寄存器,在MODBUS中只讀;
保持寄存器:PLC中用于輸出模擬量信號(hào)的寄存器,在MODBUS中可讀可寫(xiě);

二 基于ModBus-TCP的Qt案例示范


Modbus協(xié)議是一個(gè)master/slave架構(gòu)的協(xié)議。有一個(gè)節(jié)點(diǎn)是master節(jié)點(diǎn),其他使用Modbus協(xié)議參與通信的節(jié)點(diǎn)是slave節(jié)點(diǎn)。每一個(gè)slave設(shè)備都有一個(gè)唯一的地址。在串行和MB+網(wǎng)絡(luò)中,只有被指定為主節(jié)點(diǎn)的節(jié)點(diǎn)可以啟動(dòng)一個(gè)命令(在以太網(wǎng)上,任何一個(gè)設(shè)備都能發(fā)送一個(gè)Modbus命令,但是通常也只有一個(gè)主節(jié)點(diǎn)設(shè)備啟動(dòng)指令)。一般都是上位機(jī)做主站,PLC做從站。


Modbus TCP/IP協(xié)議格式

qt modbus,Qt學(xué)習(xí),程序開(kāi)發(fā),qt,c++

如上圖所示,報(bào)文主要分為兩部分,分別是協(xié)議頭(MBAP Header)和PDU。PDU 又包含功能碼(Function code)和數(shù)據(jù)(Data)兩部分。

接下來(lái)我們直接來(lái)看看Qt官方的Modbus-tcp實(shí)例:實(shí)現(xiàn)主從站的通信功能;

//Qt中幾個(gè)常用的串口modbus類

QModbusRtuSerialSlave ? ?//modbus串口通信方式下的服務(wù)器類
QModbusRtuSerialMaster ? //串口通信方式下的客戶端類
QModbusServer ? ? ? ? ? ?// QModbusServer類接收和處理modbus的請(qǐng)求。
QModbusDataUnit ? ? ? ? //存儲(chǔ)接收和發(fā)送數(shù)據(jù)的類,數(shù)據(jù)類型為1bit和16bit
QModbusReply ? ? ? ? ? ?//客戶端訪問(wèn)服務(wù)器后得到的回復(fù)(如客戶端讀服務(wù)器數(shù)據(jù)時(shí)包含數(shù)據(jù)信息)

主站關(guān)鍵代碼:


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "settingsdialog.h"
#include "writeregistermodel.h"

#include <QModbusTcpClient>
#include <QModbusRtuSerialClient>
#include <QStandardItemModel>
#include <QStatusBar>
#include <QUrl>

enum ModbusConnection {
    Serial,
    Tcp
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //初始化設(shè)置對(duì)話框類
    m_settingsDialog = new SettingsDialog(this);

    //初始化菜單操作
    initActions();

    //初始化寫(xiě)入寄存器模型
    writeModel = new WriteRegisterModel(this);
    //設(shè)置地址起始位置
    writeModel->setStartAddress(ui->writeAddress->value());
    //設(shè)置寫(xiě)入的地址位數(shù)量
    writeModel->setNumberOfValues(ui->writeSize->currentText());

    //把要寫(xiě)入的數(shù)據(jù)使用模型的方式,給視圖展示出來(lái)
    ui->writeValueTable->setModel(writeModel);
    //設(shè)置隱藏的列為第二列
    ui->writeValueTable->hideColumn(2);

    //當(dāng)視圖有更新時(shí),同步刷新列表
    connect(writeModel, &WriteRegisterModel::updateViewport,
            ui->writeValueTable->viewport(), QOverload<>::of(&QWidget::update));

    //添加寫(xiě)入數(shù)據(jù)單元的類型: 主要有五種,分別是 無(wú)效、線圈、離散輸入、輸入寄存器、保持寄存器
    ui->writeTable->addItem(tr("線圈"), QModbusDataUnit::Coils);
    ui->writeTable->addItem(tr("離散輸入"), QModbusDataUnit::DiscreteInputs);
    ui->writeTable->addItem(tr("輸入寄存器"), QModbusDataUnit::InputRegisters);
    ui->writeTable->addItem(tr("保持寄存器"), QModbusDataUnit::HoldingRegisters);

#if QT_CONFIG(modbus_serialport)
    //設(shè)置連接類型,有串口和TCP兩種方式
    ui->connectType->setCurrentIndex(0);
    //使用當(dāng)前選擇的連接方式進(jìn)行與服務(wù)器端連接
    onConnectTypeChanged(0);
#else
    // 鎖定串行端口選項(xiàng)
    ui->connectType->setCurrentIndex(1);
    //使用當(dāng)前選擇的連接方式進(jìn)行與服務(wù)器端連接
    onConnectTypeChanged(1);
    //禁用選擇框
    ui->connectType->setEnabled(false);
#endif

    //創(chuàng)建標(biāo)準(zhǔn)項(xiàng)模型對(duì)象,一共10行一列
    auto model = new QStandardItemModel(10, 1, this);
    //循環(huán)添加項(xiàng)到標(biāo)準(zhǔn)模型里面
    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");

    //把寫(xiě)入位變化及時(shí)更新至寫(xiě)入注冊(cè)模型中
    connect(ui->writeSize, &QComboBox::currentTextChanged,
            writeModel, &WriteRegisterModel::setNumberOfValues);

    //把開(kāi)始地址更新至寫(xiě)入模型中
    connect(ui->writeAddress, &QSpinBox::valueChanged, writeModel, &WriteRegisterModel::setStartAddress);

    //把起始位之前的位選擇功能禁用
    connect(ui->writeAddress, &QSpinBox::valueChanged, this, [this, model](int i) {
        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;
}

//完成每個(gè)菜單的響應(yīng)處理,即綁定菜單的信號(hào)與槽,最終在槽函數(shù)里面完成菜單的響應(yīng)邏輯
void MainWindow::initActions()
{
    ui->actionConnect->setEnabled(true);

    ui->actionDisconnect->setEnabled(false);
    ui->actionExit->setEnabled(true);
    ui->actionOptions->setEnabled(true);


    connect(ui->connectButton, &QPushButton::clicked,
            this, &MainWindow::onConnectButtonClicked);
    connect(ui->actionConnect, &QAction::triggered,
            this, &MainWindow::onConnectButtonClicked);
    connect(ui->actionDisconnect, &QAction::triggered,
            this, &MainWindow::onConnectButtonClicked);
    connect(ui->readButton, &QPushButton::clicked,
            this, &MainWindow::onReadButtonClicked);
    connect(ui->writeButton, &QPushButton::clicked,
            this, &MainWindow::onWriteButtonClicked);
    connect(ui->readWriteButton, &QPushButton::clicked,
            this, &MainWindow::onReadWriteButtonClicked);
    connect(ui->connectType, &QComboBox::currentIndexChanged,
            this, &MainWindow::onConnectTypeChanged);
    connect(ui->writeTable, &QComboBox::currentIndexChanged,
            this, &MainWindow::onWriteTableChanged);

    connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close);
    connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show);
}


//檢測(cè)連接類型
void MainWindow::onConnectTypeChanged(int index)
{
    //如果客戶端已連接,則斷開(kāi)
    if (modbusDevice) {
        modbusDevice->disconnectDevice();
        delete modbusDevice;
        modbusDevice = nullptr;
    }

    //轉(zhuǎn)換連接類型的數(shù)字為當(dāng)前定義的枚舉,即Serial或Tcp
    auto type = static_cast<ModbusConnection>(index);
    //如果是串口
    if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
        //初始化QModbusRtuSerialClient類,它表示Modbus客戶端,并且使用串行總線與Modbus服務(wù)器進(jìn)行通信
        modbusDevice = new QModbusRtuSerialClient(this);
#endif
    }
    //如果是TCP
    else if (type == Tcp) {
        //初始化QModbusTcpClient類,它是Modbus-TCP客戶端設(shè)備的接口類
        modbusDevice = new QModbusTcpClient(this);

        //如果端口設(shè)置沒(méi)有設(shè)置,則把默認(rèn)值設(shè)置為127.0.0.1:502
        if (ui->portEdit->text().isEmpty())
            ui->portEdit->setText(QLatin1String("127.0.0.1:502"));
    }

    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        statusBar()->showMessage(modbusDevice->errorString(), 5000);
    });

    if (!modbusDevice) {
        ui->connectButton->setDisabled(true);
        statusBar()->showMessage(tr("無(wú)法創(chuàng)建Modbus客戶端."), 5000);
    } else {
        connect(modbusDevice, &QModbusClient::stateChanged,
                this, &MainWindow::onModbusStateChanged);
    }
}

//配置一些主站相關(guān)的參數(shù)
void MainWindow::onConnectButtonClicked()
{
    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());
#if QT_CONFIG(modbus_serialport)
            //設(shè)置串口的幾個(gè)關(guān)鍵參數(shù):parity,baud,dataBits,stopBits
            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);
#endif
        } else {
            //設(shè)置連接的IP和端口
            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("連接失敗: ") + modbusDevice->errorString(), 5000);
        } else {
            ui->actionConnect->setEnabled(false);
            ui->actionDisconnect->setEnabled(true);
        }
    } else {
        modbusDevice->disconnectDevice();
        ui->actionConnect->setEnabled(true);
        ui->actionDisconnect->setEnabled(false);
    }
}



//監(jiān)聽(tīng)連接狀態(tài)
void MainWindow::onModbusStateChanged(int state)
{
    bool connected = (state != QModbusDevice::UnconnectedState);
    ui->actionConnect->setEnabled(!connected);
    ui->actionDisconnect->setEnabled(connected);

    if (state == QModbusDevice::UnconnectedState)
        ui->connectButton->setText(tr("連接"));
    else if (state == QModbusDevice::ConnectedState)
        ui->connectButton->setText(tr("斷開(kāi)"));
}

//讀取從站(服務(wù)器端)的數(shù)據(jù)
void MainWindow::onReadButtonClicked()
{
    if (!modbusDevice)
        return;

    ui->readValue->clear();
    statusBar()->clearMessage();

    //向服務(wù)器請(qǐng)求讀取數(shù)據(jù),參數(shù)含義為:數(shù)據(jù)單元和服務(wù)器地址
    if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
        //判斷答復(fù)狀態(tài),若還沒(méi)結(jié)束則讀取請(qǐng)求過(guò)來(lái)的數(shù)據(jù)
        if (!reply->isFinished())
            //開(kāi)始讀取數(shù)據(jù)
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
        else
            delete reply; // 廣播回復(fù)立即返回
    } else {
        statusBar()->showMessage(tr("讀取錯(cuò)誤: ") + modbusDevice->errorString(), 5000);
    }
}


//讀取數(shù)據(jù)
void MainWindow::onReadReady()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    //沒(méi)有錯(cuò)誤則開(kāi)始讀取數(shù)據(jù)
    if (reply->error() == QModbusDevice::NoError) {
        //記錄讀取的數(shù)據(jù)
        const QModbusDataUnit unit = reply->result();
        //依次取出各個(gè)位的數(shù)據(jù)
        for (qsizetype i = 0, total = unit.valueCount(); i < total; ++i) {
            const QString entry = tr("地址: %1, 值: %2").
                    arg(unit.startAddress() + i).
                    arg(QString::number(unit.value(i),unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
            //把每一個(gè)位的數(shù)據(jù)放入列表中顯示出來(lái),包括地址和值
            ui->readValue->addItem(entry);
        }
        //若通信異常,則顯示對(duì)應(yīng)的錯(cuò)誤信息
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("讀取響應(yīng)錯(cuò)誤: %1 (Modbus異常: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
    } else {
        statusBar()->showMessage(tr("讀取響應(yīng)錯(cuò)誤: %1 (編碼: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->error(), -1, 16), 5000);
    }

    reply->deleteLater();
}


//寫(xiě)數(shù)據(jù)
void MainWindow::onWriteButtonClicked()
{
    if (!modbusDevice)
        return;
    statusBar()->clearMessage();

    //獲取寫(xiě)入數(shù)據(jù)的包裝類型
    QModbusDataUnit writeUnit = writeRequest();
    //獲取需要寫(xiě)入的區(qū)域:線圈、保持寄存器
    QModbusDataUnit::RegisterType table = writeUnit.registerType();
    //writeUnit.valueCount()返回寄存器中數(shù)據(jù)單元的起始地址。
    for (qsizetype i = 0, total = writeUnit.valueCount(); i < total; ++i) {
        //如果寫(xiě)入的區(qū)域是線圈,則可以寫(xiě)入
        if (table == QModbusDataUnit::Coils)
            //寫(xiě)入線圈的0至結(jié)束地址
            writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
        else
            //寫(xiě)入保持寄存器0至結(jié)束地址
            writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
    }

    //開(kāi)始寫(xiě)入數(shù)據(jù),參數(shù)為要寫(xiě)入的封裝數(shù)據(jù)和服務(wù)器地址
    if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, ui->serverEdit->value())) {
        //檢測(cè)寫(xiě)入狀態(tài)
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::ProtocolError) {
                    statusBar()->showMessage(tr("寫(xiě)入響應(yīng)錯(cuò)誤: %1 (Modbus異常: 0x%2)")
                        .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16),
                        5000);
                } else if (reply->error() != QModbusDevice::NoError) {
                    statusBar()->showMessage(tr("寫(xiě)入響應(yīng)錯(cuò)誤: %1 (編碼: 0x%2)").
                        arg(reply->errorString()).arg(reply->error(), -1, 16), 5000);
                }
                reply->deleteLater();
            });
        } else {
            // broadcast replies return immediately
            reply->deleteLater();
        }
    } else {
        statusBar()->showMessage(tr("寫(xiě)錯(cuò)誤: ") + modbusDevice->errorString(), 5000);
    }
}

//讀寫(xiě)數(shù)據(jù): 只有保持寄存器支持同時(shí)讀寫(xiě)
void MainWindow::onReadWriteButtonClicked()
{
    if (!modbusDevice)
        return;
    ui->readValue->clear();
    statusBar()->clearMessage();

    //寫(xiě)請(qǐng)求
    QModbusDataUnit writeUnit = writeRequest();
    QModbusDataUnit::RegisterType table = writeUnit.registerType();
    for (qsizetype i = 0, total = writeUnit.valueCount(); i < total; ++i) {
        if (table == QModbusDataUnit::Coils)
            writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
        else
            writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
    }

    //讀寫(xiě)請(qǐng)求
    if (auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeUnit,
        ui->serverEdit->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("讀錯(cuò)誤: ") + modbusDevice->errorString(), 5000);
    }
}


//檢測(cè)寫(xiě)入?yún)^(qū)域的選擇: 線圈、離散輸入、輸入寄存器、保持寄存器
void MainWindow::onWriteTableChanged(int index)
{
    //判斷是線圈還是保持寄存器
    const bool coilsOrHolding = index == 0 || index == 3;
    //如果選擇的是線圈或保持寄存器
    if (coilsOrHolding) {
        //隱藏第2、3列
        ui->writeValueTable->setColumnHidden(1, index != 0);
        ui->writeValueTable->setColumnHidden(2, index != 3);
        //根據(jù)列內(nèi)容的大小調(diào)整第一列的大小。
        ui->writeValueTable->resizeColumnToContents(0);
    }

    //若寫(xiě)入?yún)^(qū)域?yàn)楸3旨拇嫫鳎瑒t啟用讀寫(xiě)按鈕
    ui->readWriteButton->setEnabled(index == 3);
    //若寫(xiě)入?yún)^(qū)域?yàn)榫€圈或保持寄存器,則啟用讀寫(xiě)按鈕
    ui->writeButton->setEnabled(coilsOrHolding);
    //若寫(xiě)入?yún)^(qū)域?yàn)楸3旨拇嫫鳎瑒t啟用寫(xiě)入?yún)^(qū)域的所有字節(jié)輸入框
    ui->writeGroupBox->setEnabled(coilsOrHolding);
}



//包裝需要讀取的數(shù)據(jù)
QModbusDataUnit MainWindow::readRequest() const
{
    //獲取當(dāng)前需要讀取的區(qū)域:線圈、離散輸入、輸入寄存器、保持寄存器
    const auto table =
        static_cast<QModbusDataUnit::RegisterType>(ui->writeTable->currentData().toInt());

    int startAddress = ui->readAddress->value();
    Q_ASSERT(startAddress >= 0 && startAddress < 10);

    // 不要超過(guò)10個(gè)條目,在需要讀取的字節(jié)數(shù)與有效數(shù)據(jù)的字節(jié)數(shù)之間獲取較小的值
    quint16 numberOfEntries = qMin(ui->readSize->currentText().toUShort(), quint16(10 - startAddress));
    //返回需要讀取的數(shù)據(jù)單元,參數(shù)的含義分別是要讀取的區(qū)域、開(kāi)始地址、讀取的字節(jié)數(shù)
    return QModbusDataUnit(table, startAddress, numberOfEntries);
}




//包裝寫(xiě)入的數(shù)據(jù)
QModbusDataUnit MainWindow::writeRequest() const
{
    //獲取寫(xiě)入數(shù)據(jù)的區(qū)域類型:線圈、離散輸入、輸入寄存器、保持寄存器
    const auto table =
        static_cast<QModbusDataUnit::RegisterType>(ui->writeTable->currentData().toInt());
     //開(kāi)始地址
    int startAddress = ui->writeAddress->value();
    Q_ASSERT(startAddress >= 0 && startAddress < 10);

    //  不要超過(guò)10個(gè)條目,在需要讀取的字節(jié)數(shù)與有效數(shù)據(jù)的字節(jié)數(shù)之間獲取較小的值
    quint16 numberOfEntries = qMin(ui->writeSize->currentText().toUShort(), quint16(10 - startAddress));
    //返回需要讀取的數(shù)據(jù)單元,參數(shù)的含義分別是要讀取的區(qū)域、開(kāi)始地址、讀取的字節(jié)數(shù)
    return QModbusDataUnit(table, startAddress, numberOfEntries);
}



從站關(guān)鍵代碼:


#include "mainwindow.h"
#include "settingsdialog.h"
#include "ui_mainwindow.h"

#include <QModbusRtuSerialServer>
#include <QModbusTcpServer>
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#include <QStatusBar>
#include <QUrl>

//枚舉連接類型
enum ModbusConnection {
    Serial,
    Tcp
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setupWidgetContainers();

    //設(shè)置默認(rèn)連接類型
#if QT_CONFIG(modbus_serialport)
    ui->connectType->setCurrentIndex(0);
    onCurrentConnectTypeChanged(0);
#else
    // 鎖定串行端口選項(xiàng),設(shè)置連接類型
    ui->connectType->setCurrentIndex(1);
    onCurrentConnectTypeChanged(1);
    ui->connectType->setEnabled(false);
#endif

    m_settingsDialog = new SettingsDialog(this);
    initActions();
}

MainWindow::~MainWindow()
{
    if (modbusDevice)
        modbusDevice->disconnectDevice();
    delete modbusDevice;

    delete ui;
}

//初始化菜單
void MainWindow::initActions()
{
    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);
    ui->actionExit->setEnabled(true);
    ui->actionOptions->setEnabled(true);

    connect(ui->connectButton, &QPushButton::clicked,
            this, &MainWindow::onConnectButtonClicked);
    connect(ui->actionConnect, &QAction::triggered,
            this, &MainWindow::onConnectButtonClicked);
    connect(ui->actionDisconnect, &QAction::triggered,
            this, &MainWindow::onConnectButtonClicked);
    connect(ui->connectType, &QComboBox::currentIndexChanged,
            this, &MainWindow::onCurrentConnectTypeChanged);

    connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close);
    connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show);
}

//類型變化檢測(cè),根據(jù)選中的不同連接類型創(chuàng)建不同的對(duì)象
void MainWindow::onCurrentConnectTypeChanged(int index)
{
    if (modbusDevice) {
        modbusDevice->disconnect();
        delete modbusDevice;
        modbusDevice = nullptr;
    }

    //獲取連接類型并強(qiáng)制轉(zhuǎn)換為枚舉
    auto type = static_cast<ModbusConnection>(index);
    //串口連接方式
    if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
        modbusDevice = new QModbusRtuSerialServer(this);
#endif
    }
    //TCP連接方式
    else if (type == Tcp) {
        modbusDevice = new QModbusTcpServer(this);
        if (ui->portEdit->text().isEmpty())
            ui->portEdit->setText(QLatin1String("127.0.0.1:502"));
    }
    //若類型為serial時(shí),啟用僅監(jiān)聽(tīng)按鈕,否則禁用
    ui->listenOnlyBox->setEnabled(type == Serial);

    //如果對(duì)象為空,則提示錯(cuò)誤信息
    if (!modbusDevice) {
        ui->connectButton->setDisabled(true);
        statusBar()->showMessage(tr("Could not create Modbus server."), 5000);
    } else {
        //對(duì)象創(chuàng)建成功后,初始化各個(gè)數(shù)據(jù)區(qū)的參數(shù)設(shè)置
        QModbusDataUnitMap reg;
        reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 10 });
        reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 10 });
        reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 10 });
        reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 10 });

        modbusDevice->setMap(reg);

        connect(modbusDevice, &QModbusServer::dataWritten,
                this, &MainWindow::updateWidgets);
        connect(modbusDevice, &QModbusServer::stateChanged,
                this, &MainWindow::onStateChanged);
        connect(modbusDevice, &QModbusServer::errorOccurred,
                this, &MainWindow::handleDeviceError);

        connect(ui->listenOnlyBox, &QCheckBox::toggled, this, [this](bool toggled) {
            if (modbusDevice)
                modbusDevice->setValue(QModbusServer::ListenOnlyMode, toggled);
        });
        emit ui->listenOnlyBox->toggled(ui->listenOnlyBox->isChecked());
        connect(ui->setBusyBox, &QCheckBox::toggled, this, [this](bool toggled) {
            if (modbusDevice)
                modbusDevice->setValue(QModbusServer::DeviceBusy, toggled ? 0xffff : 0x0000);
        });
        emit ui->setBusyBox->toggled(ui->setBusyBox->isChecked());

        setupDeviceData();
    }
}

//處理設(shè)備錯(cuò)誤
void MainWindow::handleDeviceError(QModbusDevice::Error newError)
{
    if (newError == QModbusDevice::NoError || !modbusDevice)
        return;

    statusBar()->showMessage(modbusDevice->errorString(), 5000);
}


//連接客戶端
void MainWindow::onConnectButtonClicked()
{
    //是否處于未連接狀態(tài)
    bool intendToConnect = (modbusDevice->state() == QModbusDevice::UnconnectedState);

    statusBar()->clearMessage();

    //按照不同的連接方式創(chuàng)建不同的對(duì)象
    if (intendToConnect) {
        //若為serial連接方式,則設(shè)置相關(guān)參數(shù)
        if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                ui->portEdit->text());
#if QT_CONFIG(modbus_serialport)
            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);
#endif
        }
        //拖尾TCP方式,則設(shè)置其IP和端口
        else {
            const QUrl url = QUrl::fromUserInput(ui->portEdit->text());
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
        }
        //設(shè)置服務(wù)器地址
        modbusDevice->setServerAddress(ui->serverEdit->text().toInt());
        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);
    }
}

//連接狀態(tài)檢測(cè)
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"));
}

//線圈變化處理函數(shù)
void MainWindow::coilChanged(int id)
{
    QAbstractButton *button = coilButtons.button(id);
    bitChanged(id, QModbusDataUnit::Coils, button->isChecked());
}

//離散輸入量處理函數(shù)
void MainWindow::discreteInputChanged(int id)
{
    QAbstractButton *button = discreteButtons.button(id);
    bitChanged(id, QModbusDataUnit::DiscreteInputs, button->isChecked());
}

//bit位處理函數(shù)
void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool value)
{
    if (!modbusDevice)
        return;

    if (!modbusDevice->setData(table, quint16(id), value))
        statusBar()->showMessage(tr("Could not set data: ") + modbusDevice->errorString(), 5000);
}

//設(shè)置寄存器
void MainWindow::setRegister(const QString &value)
{
    if (!modbusDevice)
        return;

    const QString objectName = QObject::sender()->objectName();
    if (registers.contains(objectName)) {
        bool ok = true;
        const quint16 id = quint16(QObject::sender()->property("ID").toUInt());
        if (objectName.startsWith(QStringLiteral("inReg")))
            ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, value.toUShort(&ok, 16));
        else if (objectName.startsWith(QStringLiteral("holdReg")))
            ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, value.toUShort(&ok, 16));

        if (!ok)
            statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(),
                                     5000);
    }
}

//更新窗口部件
void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size)
{
    for (int i = 0; i < size; ++i) {
        quint16 value;
        QString text;
        switch (table) {
        case QModbusDataUnit::Coils:
            modbusDevice->data(QModbusDataUnit::Coils, quint16(address + i), &value);
            coilButtons.button(address + i)->setChecked(value);
            break;
        case QModbusDataUnit::HoldingRegisters:
            modbusDevice->data(QModbusDataUnit::HoldingRegisters, quint16(address + i), &value);
            registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text
                .setNum(value, 16));
            break;
        default:
            break;
        }
    }
}



//設(shè)置設(shè)備數(shù)據(jù)
void MainWindow::setupDeviceData()
{
    if (!modbusDevice)
        return;

    //遍歷所有bit位信息寫(xiě)入寄存器
    for (quint16 i = 0; i < coilButtons.buttons().count(); ++i)
        modbusDevice->setData(QModbusDataUnit::Coils, i, coilButtons.button(i)->isChecked());

    for (quint16 i = 0; i < discreteButtons.buttons().count(); ++i) {
        modbusDevice->setData(QModbusDataUnit::DiscreteInputs, i,
            discreteButtons.button(i)->isChecked());
    }

    bool ok;
    for (QLineEdit *widget : qAsConst(registers)) {
        if (widget->objectName().startsWith(QStringLiteral("inReg"))) {
            modbusDevice->setData(QModbusDataUnit::InputRegisters, quint16(widget->property("ID").toUInt()),
                widget->text().toUShort(&ok, 16));
        } else if (widget->objectName().startsWith(QStringLiteral("holdReg"))) {
            modbusDevice->setData(QModbusDataUnit::HoldingRegisters, quint16(widget->property("ID").toUInt()),
                widget->text().toUShort(&ok, 16));
        }
    }
}

//設(shè)置窗口容器
void MainWindow::setupWidgetContainers()
{
    coilButtons.setExclusive(false);
    discreteButtons.setExclusive(false);

    QRegularExpression regexp(QStringLiteral("coils_(?<ID>\\d+)"));
    const QList<QCheckBox *> coils = findChildren<QCheckBox *>(regexp);
    for (QCheckBox *cbx : coils)
        coilButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
    connect(&coilButtons, &QButtonGroup::idClicked, this, &MainWindow::coilChanged);

    regexp.setPattern(QStringLiteral("disc_(?<ID>\\d+)"));
    const QList<QCheckBox *> discs = findChildren<QCheckBox *>(regexp);
    for (QCheckBox *cbx : discs)
        discreteButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
    connect(&discreteButtons, &QButtonGroup::idClicked, this, &MainWindow::discreteInputChanged);

    regexp.setPattern(QLatin1String("(in|hold)Reg_(?<ID>\\d+)"));
    const QList<QLineEdit *> qle = findChildren<QLineEdit *>(regexp);
    for (QLineEdit *lineEdit : qle) {
        registers.insert(lineEdit->objectName(), lineEdit);
        lineEdit->setProperty("ID", regexp.match(lineEdit->objectName()).captured("ID").toInt());
        lineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("[0-9a-f]{0,4}"),
            QRegularExpression::CaseInsensitiveOption), this));
        connect(lineEdit, &QLineEdit::textChanged, this, &MainWindow::setRegister);
    }
}

運(yùn)行效果:

qt modbus,Qt學(xué)習(xí),程序開(kāi)發(fā),qt,c++

完整代碼下載鏈接:

https://download.csdn.net/download/XiaoWang_csdn/87616287

下一篇博客:

https://blog.csdn.net/XiaoWang_csdn/article/details/129789509

?上一篇博客:

Qt6教程之三(14) 串口通信_(tái)折騰猿王申兵的博客-CSDN博客文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-708403.html

到了這里,關(guān)于Qt6教程之三(15) Modbus通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Qt6教程之三(13) TCP/IP通訊與socket編程

    Qt6教程之三(13) TCP/IP通訊與socket編程

    目錄 一 前言 二 TCP/IP協(xié)議架構(gòu)和通信原理 三?TCP/IP的連接與斷開(kāi)過(guò)程 四 Qt中開(kāi)發(fā)TCP/IP原理概述 五 完整實(shí)例代碼示范 在軟件開(kāi)發(fā)中,常用的技術(shù)體系里面網(wǎng)絡(luò)通信屬于最重要的 “聯(lián)通” 技術(shù),是必須要掌握的技術(shù)。 那為什么網(wǎng)絡(luò)通信如此重要呢,我想大概有以下幾點(diǎn): 不

    2024年02月12日
    瀏覽(25)
  • 用C++QT實(shí)現(xiàn)一個(gè)modbus rtu通訊程序框架

    下面是一個(gè)簡(jiǎn)單的Modbus RTU通訊程序框架的示例,使用C++和QT來(lái)實(shí)現(xiàn): 具體的數(shù)據(jù)處理將根據(jù)需求進(jìn)行擴(kuò)展和實(shí)現(xiàn),如寫(xiě)入數(shù)據(jù)和處理異常等。另外,需要根據(jù)實(shí)際情況設(shè)置正確的串口參數(shù)和設(shè)備地址,并確保與Modbus設(shè)備的正確連接。在編譯和運(yùn)行程序之前,還需要在項(xiàng)目的

    2024年02月06日
    瀏覽(27)
  • modbus介紹、環(huán)境搭建測(cè)試與qt下串口/Tcp的demo工程測(cè)試

    modbus介紹、環(huán)境搭建測(cè)試與qt下串口/Tcp的demo工程測(cè)試

    1.簡(jiǎn)介 ??Modbus是一種串行通信協(xié)議,于1979年為使用可編程邏輯控制器(PLC)通信而發(fā)表。Modbus已經(jīng)成為工業(yè)領(lǐng)域通信協(xié)議的業(yè)界標(biāo)準(zhǔn)(De facto),并且現(xiàn)在是工業(yè)電子設(shè)備之間常用的連接方式,Modbus協(xié)議目前存在用于串口、以太網(wǎng)以及其他支持互聯(lián)網(wǎng)協(xié)議的網(wǎng)絡(luò)的版本。 2

    2024年02月11日
    瀏覽(15)
  • Qt/C++編寫(xiě)物聯(lián)網(wǎng)組件/支持modbus/rtu/tcp/udp/websocket/mqtt/多線程采集

    Qt/C++編寫(xiě)物聯(lián)網(wǎng)組件/支持modbus/rtu/tcp/udp/websocket/mqtt/多線程采集

    支持多種協(xié)議,包括Modbus_Rtu_Com/Modbus_Rtu_Tcp/Modbus_Rtu_Udp/Modbus_Rtu_Web/Modbus_Tcp/Modbus_Udp/Modbus_Web等,其中web指websocket。 支持多種采集通訊方式,包括串口和網(wǎng)絡(luò)等,可自由拓展其他方式。 自定義采集間隔(精確到毫秒)和超時(shí)次數(shù),超時(shí)后自動(dòng)將離線的文件從輪詢隊(duì)列中移除,加

    2024年02月08日
    瀏覽(22)
  • Qt/C++編寫(xiě)物聯(lián)網(wǎng)管理平臺(tái)(支持win/linux/mac/嵌入式linux/modbus等)

    Qt/C++編寫(xiě)物聯(lián)網(wǎng)管理平臺(tái)(支持win/linux/mac/嵌入式linux/modbus等)

    這個(gè)物聯(lián)網(wǎng)綜合管理平臺(tái)前后迭代了五年,一點(diǎn)一滴慢慢積累起來(lái),從最開(kāi)始的只有modbus串口協(xié)議解析以及簡(jiǎn)單的表格顯示數(shù)據(jù),慢慢的逐漸增加了tcp_rtu支持,用戶管理模塊,地圖監(jiān)控模塊,而后為了拓展性又做了云端數(shù)據(jù)同步,網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)發(fā)等。編寫(xiě)這個(gè)系統(tǒng)的過(guò)程中,真

    2024年02月10日
    瀏覽(229)
  • 【QT教程】QT6物聯(lián)網(wǎng)應(yīng)用

    QT6物聯(lián)網(wǎng)應(yīng)用 使用AI技術(shù)輔助生成 QT界面美化視頻課程 QT性能優(yōu)化視頻課程 QT原理與源碼分析視頻課程 QT QML C++擴(kuò)展開(kāi)發(fā)視頻課程 免費(fèi)QT視頻課程 您可以看免費(fèi)1000+個(gè)QT技術(shù)視頻 免費(fèi)QT視頻課程 QT統(tǒng)計(jì)圖和QT數(shù)據(jù)可視化視頻免費(fèi)看 免費(fèi)QT視頻課程 QT性能優(yōu)化視頻免費(fèi)看 免費(fèi)QT視

    2024年04月25日
    瀏覽(23)
  • QT6 for android 安裝教程記錄(版本Qt6.5.2)

    QT6 for android 安裝教程記錄(版本Qt6.5.2)

    本文記錄首次安裝QT for andriod的詳細(xì)記錄。 網(wǎng)上的信息和資料非常多,收集和整理以及遇到的問(wèn)題也各異,對(duì)新手首次接觸相關(guān)開(kāi)發(fā)和部署環(huán)境并不是清晰,因此,特將相關(guān)詳細(xì)配置記錄。 首先,開(kāi)發(fā)QT for andriod 不建議使用QT5.15的版本,因?yàn)樵摪姹静荒軈^(qū)分相關(guān)的CPU架構(gòu),而

    2024年02月03日
    瀏覽(21)
  • Qt6.2教程——3.Qt信號(hào)和槽

    信號(hào)和槽是Qt中一個(gè)強(qiáng)大的特性,用于處理對(duì)象之間的通信。它們是一種事件處理機(jī)制,允許一個(gè)對(duì)象在某個(gè)事件發(fā)生時(shí)通知另一個(gè)對(duì)象。 定義 : 信號(hào)是一個(gè)QObject的成員函數(shù),當(dāng)某個(gè)特定事件發(fā)生時(shí),它被自動(dòng)調(diào)用。它可以與一個(gè)或多個(gè)槽關(guān)聯(lián)。 聲明 : 在Qt類的聲明中,信號(hào)

    2024年02月10日
    瀏覽(19)
  • Qt6教程之一 Qt介紹及準(zhǔn)備工作

    Qt6教程之一 Qt介紹及準(zhǔn)備工作

    在正式開(kāi)始之前,需要在自己電腦上面搭建好Qt的開(kāi)發(fā)環(huán)境,本教程使用的Qt開(kāi)發(fā)環(huán)境為Qt6.2 。 那話不多說(shuō),咋們開(kāi)始做準(zhǔn)備工作吧! 第一步:查看電腦硬件配置及操作系統(tǒng) 如果有一臺(tái)較好配置的電腦,那么無(wú)疑用起來(lái)是最舒心的,推薦的最佳電腦配置: 由于Qt是為跨平臺(tái)而

    2024年02月09日
    瀏覽(23)
  • Qt6入門教程 6:Qt元對(duì)象系統(tǒng)

    Qt6入門教程 6:Qt元對(duì)象系統(tǒng)

    目錄 一.什么是Qt元對(duì)象系統(tǒng)? 二.編譯時(shí)Qt Creator偷摸做了哪些事情? 1.uic 2.rcc 3.moc Qt中的元對(duì)象系統(tǒng)(Meta-Object System)提供了對(duì)象間通信的信號(hào)和槽機(jī)制、運(yùn)行時(shí)類型信息和動(dòng)態(tài)屬性系統(tǒng)。元對(duì)象系統(tǒng)是基于以下3個(gè)條件的: ●該類必須繼承自QObject類; ●必須在類的私有聲

    2024年01月18日
    瀏覽(28)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包