QT blockingmaster例子学习

dialog.h:

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

#include "masterthread.h"
//这里的写法有点。。为什么不直接加头文件
QT_BEGIN_NAMESPACE

class QLabel;
class QLineEdit;
class QSpinBox;
class QPushButton;
class QComboBox;

QT_END_NAMESPACE

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);

private slots:
    void transaction();
    void showResponse(const QString &s);
    void processError(const QString &s);
    void processTimeout(const QString &s);

private:
    void setControlsEnabled(bool enable);

private:
    int transactionCount;
    QLabel *serialPortLabel;                  //声明了各类控件
    QComboBox *serialPortComboBox;
    QLabel *waitResponseLabel;
    QSpinBox *waitResponseSpinBox;
    QLabel *requestLabel;
    QLineEdit *requestLineEdit;
    QLabel *trafficLabel;
    QLabel *statusLabel;
    QPushButton *runButton;

    MasterThread thread;                    //创建了一个线程类
};

#endif // DIALOG_H

masterthread.h:

#ifndef MASTERTHREAD_H
#define MASTERTHREAD_H

#include <QThread>
#include <QMutex>
#include <QWaitCondition>

//! [0]
class MasterThread : public QThread
{
    Q_OBJECT

public:
    explicit MasterThread(QObject *parent = nullptr);
    ~MasterThread();

    void transaction(const QString &portName, int waitTimeout, const QString &request);        //用来传递GUI界面的信息函数
    void run() Q_DECL_OVERRIDE;                                         //线程运行函数

signals:
    void response(const QString &s);                                      //一些信号:响应、错误、时间
    void error(const QString &s);
    void timeout(const QString &s);

private:
    QString portName;                                               //需要操作记录的私有变量
    QString request;
    int waitTimeout;
    QMutex mutex;                                                                                    //互斥锁,保护上面的私有变量
    QWaitCondition cond;                                                                             //条件变量,用来同步
    bool quit;
};
//! [0]

#endif // MASTERTHREAD_H

dialog.cpp:

#include "dialog.h"

#include <QLabel>
#include <QLineEdit>
#include <QComboBox>
#include <QSpinBox>
#include <QPushButton>
#include <QGridLayout>

#include <QtSerialPort/QSerialPortInfo>

QT_USE_NAMESPACE

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , transactionCount(0)
    , serialPortLabel(new QLabel(tr("Serial port:")))
    , serialPortComboBox(new QComboBox())
    , waitResponseLabel(new QLabel(tr("Wait response, msec:")))
    , waitResponseSpinBox(new QSpinBox())
    , requestLabel(new QLabel(tr("Request:")))
    , requestLineEdit(new QLineEdit(tr("Who are you?")))
    , trafficLabel(new QLabel(tr("No traffic.")))
    , statusLabel(new QLabel(tr("Status: Not running.")))
    , runButton(new QPushButton(tr("Start")))
{
    const auto infos = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo &info : infos)
        serialPortComboBox->addItem(info.portName());

    waitResponseSpinBox->setRange(0, 10000);
    waitResponseSpinBox->setValue(1000);

    auto mainLayout = new QGridLayout;
    mainLayout->addWidget(serialPortLabel, 0, 0);
    mainLayout->addWidget(serialPortComboBox, 0, 1);
    mainLayout->addWidget(waitResponseLabel, 1, 0);
    mainLayout->addWidget(waitResponseSpinBox, 1, 1);
    mainLayout->addWidget(runButton, 0, 2, 2, 1);
    mainLayout->addWidget(requestLabel, 2, 0);
    mainLayout->addWidget(requestLineEdit, 2, 1, 1, 3);
    mainLayout->addWidget(trafficLabel, 3, 0, 1, 4);
    mainLayout->addWidget(statusLabel, 4, 0, 1, 5);
    setLayout(mainLayout);

    setWindowTitle(tr("Blocking Master"));
    serialPortComboBox->setFocus();

    connect(runButton, &QPushButton::clicked, this, &Dialog::transaction);
    connect(&thread, &MasterThread::response, this, &Dialog::showResponse);
    connect(&thread, &MasterThread::error, this, &Dialog::processError);
    connect(&thread, &MasterThread::timeout, this, &Dialog::processTimeout);
}

void Dialog::transaction()
{
    setControlsEnabled(false);
    statusLabel->setText(tr("Status: Running, connected to port %1.")
                         .arg(serialPortComboBox->currentText()));
    thread.transaction(serialPortComboBox->currentText(),
                       waitResponseSpinBox->value(),
                       requestLineEdit->text());
}

void Dialog::showResponse(const QString &s)
{
    setControlsEnabled(true);
    trafficLabel->setText(tr("Traffic, transaction #%1:"
                             "

-request: %2"
                             "

-response: %3")
                          .arg(++transactionCount).arg(requestLineEdit->text()).arg(s));
}

void Dialog::processError(const QString &s)
{
    setControlsEnabled(true);
    statusLabel->setText(tr("Status: Not running, %1.").arg(s));
    trafficLabel->setText(tr("No traffic."));
}

void Dialog::processTimeout(const QString &s)
{
    setControlsEnabled(true);
    statusLabel->setText(tr("Status: Running, %1.").arg(s));
    trafficLabel->setText(tr("No traffic."));
}

void Dialog::setControlsEnabled(bool enable)
{
    runButton->setEnabled(enable);
    serialPortComboBox->setEnabled(enable);
    waitResponseSpinBox->setEnabled(enable);
    requestLineEdit->setEnabled(enable);
}

masterthread.cpp:

#include "masterthread.h"

#include <QtSerialPort/QSerialPort>

#include <QTime>

QT_USE_NAMESPACE

MasterThread::MasterThread(QObject *parent)
    : QThread(parent), waitTimeout(0), quit(false)
{
}

//! [0]
MasterThread::~MasterThread()
{
    mutex.lock();
    quit = true;
    cond.wakeOne();
    mutex.unlock();
    wait();
}
//! [0]

//! [1] //! [2]
void MasterThread::transaction(const QString &portName, int waitTimeout, const QString &request)
{
    //! [1]
    QMutexLocker locker(&mutex);                            //用QMutexLocker锁住函数内的操作
    this->portName = portName;
    this->waitTimeout = waitTimeout;
    this->request = request;
    //! [3]
    if (!isRunning())                                  //判断线程是否启动
        start();
    else
        cond.wakeOne();                                 //已经启动则唤醒线程
}
//! [2] //! [3]

//! [4]
void MasterThread::run()
{
    bool currentPortNameChanged = false;          //临时变量

    mutex.lock();
    //! [4] //! [5]
    QString currentPortName;                  //临时变量
    if (currentPortName != portName) {
        currentPortName = portName;
        currentPortNameChanged = true;
    }

    int currentWaitTimeout = waitTimeout;
    QString currentRequest = request;
    mutex.unlock();                      //到这里把私有信息传递进去了
    //! [5] //! [6]
    QSerialPort serial;                    //创建了一个串口类

    while (!quit) {
        //![6] //! [7]
        if (currentPortNameChanged) {            //如果改了名字
            serial.close();
            serial.setPortName(currentPortName);      //重新打开

            if (!serial.open(QIODevice::ReadWrite)) {        //判断是否打开成功
                emit error(tr("Can't open %1, error code %2")
                           .arg(portName).arg(serial.error()));
                return;
            }
        }
        //! [7] //! [8]
        // write request
        QByteArray requestData = currentRequest.toLocal8Bit();      //请求的消息转成byte格式
        serial.write(requestData);                      //发送数据
        if (serial.waitForBytesWritten(waitTimeout)) {           //等待waitTimeout时间发送数据
            //! [8] //! [10]
            // read response
            if (serial.waitForReadyRead(currentWaitTimeout)) {      //等待waitTimeout时间读取串口数据
                QByteArray responseData = serial.readAll();        //将读取数据写到responseData中
                while (serial.waitForReadyRead(10))             //再等10秒
                    responseData += serial.readAll();            //写进responseData中

                QString response(responseData);               //这里又转成QString类型了
                //! [12]
                emit this->response(response);               //发送response信号
                //! [10] //! [11] //! [12]
            } else {
                emit timeout(tr("Wait read response timeout %1")
                             .arg(QTime::currentTime().toString()));    //发送响应延时信号
            }
            //! [9] //! [11]
        } else {
            emit timeout(tr("Wait write request timeout %1")
                         .arg(QTime::currentTime().toString()));         //发送写延时信号
        }
        //! [9]  //! [13]
        mutex.lock();
        cond.wait(&mutex);                           //这里又等待了?怎么唤醒?第二次按按钮时唤醒,这是一个循环。再次点击又运行一次
        if (currentPortName != portName) {
            currentPortName = portName;
            currentPortNameChanged = true;
        } else {
            currentPortNameChanged = false;
        }
        currentWaitTimeout = waitTimeout;
        currentRequest = request;
        mutex.unlock();
    }
    //! [13]
}
无欲速,无见小利。欲速,则不达;见小利,则大事不成。
原文地址:https://www.cnblogs.com/ch122633/p/7497379.html