Qt 學習筆記 - 第三章 - Qt的三駕馬車之一 - 串列埠程式設計 + 程式打包成Windows軟體

Dandelion_000發表於2023-03-10

Qt 學習筆記全系列傳送門:

1、建立專案

實現串列埠助手

  • 建立 Qt Widgets Application 專案 seial

  • 基類選擇 Widget

2、UI

  • UI設計

    1. 接收框元件,在分類 Input Widgets 中,Plain Text Edit 元件(QPlainTextEdit),雙擊可以編輯選項,置頂項為預設選擇屬性,勾選元件的只讀屬性 readOnly
    2. 屬性選擇,在分類 Input Widgets 中,Combo Box 元件(QComboBox)
    3. 傳送框,在分類在 Input Widgets 中,Line Edit 元件(QLineEdit)
    4. 資訊框,寫個廣告之類的可以使用,在分類 中,Group Box 元件(QGroupBox)
    5. 控制元件改名
  • UI程式碼展示

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Widget</class>
     <widget class="QWidget" name="Widget">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>800</width>
        <height>480</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>Widget</string>
      </property>
      <widget class="QWidget" name="layoutWidget">
       <property name="geometry">
        <rect>
         <x>31</x>
         <y>31</y>
         <width>737</width>
         <height>385</height>
        </rect>
       </property>
       <layout class="QGridLayout" name="gridLayout_3">
        <item row="0" column="0">
         <widget class="QPlainTextEdit" name="recvEdit">
          <property name="readOnly">
           <bool>true</bool>
          </property>
         </widget>
        </item>
        <item row="1" column="0">
         <spacer name="verticalSpacer">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="2" column="0">
         <layout class="QGridLayout" name="gridLayout_2">
          <item row="0" column="0">
           <layout class="QGridLayout" name="gridLayout">
            <item row="0" column="0">
             <widget class="QLabel" name="label_2">
              <property name="text">
               <string>波特率</string>
              </property>
             </widget>
            </item>
            <item row="0" column="1">
             <widget class="QComboBox" name="baundrateCb">
              <item>
               <property name="text">
                <string>4800</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>9600</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>115200</string>
               </property>
              </item>
             </widget>
            </item>
            <item row="1" column="0">
             <widget class="QLabel" name="label">
              <property name="text">
               <string>串列埠號</string>
              </property>
             </widget>
            </item>
            <item row="1" column="1">
             <widget class="QComboBox" name="serialCb"/>
            </item>
            <item row="2" column="0">
             <widget class="QLabel" name="label_5">
              <property name="text">
               <string>資料位</string>
              </property>
             </widget>
            </item>
            <item row="2" column="1">
             <widget class="QComboBox" name="dataCb">
              <item>
               <property name="text">
                <string>5</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>6</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>7</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>8</string>
               </property>
              </item>
             </widget>
            </item>
            <item row="3" column="0">
             <widget class="QLabel" name="label_4">
              <property name="text">
               <string>停止位</string>
              </property>
             </widget>
            </item>
            <item row="3" column="1">
             <widget class="QComboBox" name="stopCb">
              <item>
               <property name="text">
                <string>1</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>1.5</string>
               </property>
              </item>
              <item>
               <property name="text">
                <string>2</string>
               </property>
              </item>
             </widget>
            </item>
            <item row="4" column="0">
             <widget class="QLabel" name="label_3">
              <property name="text">
               <string>校驗位</string>
              </property>
             </widget>
            </item>
            <item row="4" column="1">
             <widget class="QComboBox" name="checkCb">
              <item>
               <property name="text">
                <string>none</string>
               </property>
              </item>
             </widget>
            </item>
           </layout>
          </item>
          <item row="0" column="1">
           <spacer name="horizontalSpacer_4">
            <property name="orientation">
             <enum>Qt::Horizontal</enum>
            </property>
            <property name="sizeHint" stdset="0">
             <size>
              <width>40</width>
              <height>20</height>
             </size>
            </property>
           </spacer>
          </item>
          <item row="0" column="2">
           <layout class="QVBoxLayout" name="verticalLayout_2">
            <item>
             <widget class="QGroupBox" name="groupBox">
              <property name="title">
               <string>歡迎使用,這是一個資訊框</string>
              </property>
              <widget class="QLabel" name="label_6">
               <property name="geometry">
                <rect>
                 <x>120</x>
                 <y>30</y>
                 <width>161</width>
                 <height>21</height>
                </rect>
               </property>
               <property name="text">
                <string>demo info...</string>
               </property>
              </widget>
             </widget>
            </item>
            <item>
             <widget class="QLineEdit" name="sendEdit"/>
            </item>
            <item>
             <layout class="QHBoxLayout" name="horizontalLayout_6">
              <item>
               <widget class="QPushButton" name="openBt">
                <property name="text">
                 <string>開啟串列埠</string>
                </property>
               </widget>
              </item>
              <item>
               <spacer name="horizontalSpacer">
                <property name="orientation">
                 <enum>Qt::Horizontal</enum>
                </property>
                <property name="sizeHint" stdset="0">
                 <size>
                  <width>40</width>
                  <height>20</height>
                 </size>
                </property>
               </spacer>
              </item>
              <item>
               <widget class="QPushButton" name="closeBt">
                <property name="text">
                 <string>關閉串列埠</string>
                </property>
               </widget>
              </item>
              <item>
               <spacer name="horizontalSpacer_2">
                <property name="orientation">
                 <enum>Qt::Horizontal</enum>
                </property>
                <property name="sizeHint" stdset="0">
                 <size>
                  <width>40</width>
                  <height>20</height>
                 </size>
                </property>
               </spacer>
              </item>
              <item>
               <widget class="QPushButton" name="sendBt">
                <property name="text">
                 <string>傳送</string>
                </property>
               </widget>
              </item>
              <item>
               <spacer name="horizontalSpacer_3">
                <property name="orientation">
                 <enum>Qt::Horizontal</enum>
                </property>
                <property name="sizeHint" stdset="0">
                 <size>
                  <width>40</width>
                  <height>20</height>
                 </size>
                </property>
               </spacer>
              </item>
              <item>
               <widget class="QPushButton" name="clearBt">
                <property name="text">
                 <string>清空</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
           </layout>
          </item>
         </layout>
        </item>
       </layout>
      </widget>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>
    

3、邏輯功能

  1. 在工程檔案中引入serialport

    QT       += core gui serialport
    
  2. 獲取串列埠資訊並展示到頁面上,在目前 UI 對應的 cpp 的構造中進行

    說明:需要連線微控制器才能顯示串列埠號

    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
        QStringList serialNamePorts;
    
        // QSerialPort 是串列埠資訊類,用於存放串列埠資訊
        // QSerialPortInfo::availablePorts() 自動搜尋可用串列埠,返回串列埠資訊類物件的陣列
        foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
    
            // 將得到的串列埠資訊的串列埠號加入到 QStringList 中
            serialNamePorts<<info.portName();
        }
    
        // 將可用串列埠的列表顯示到頁面的下拉框中
        ui->serialCb->addItems(serialNamePorts);
    }
    
  3. 其他控制元件的邏輯功能

    • 點選開啟串列埠時對串列埠進行初始化

      • 對串列埠的宣告和建立

        • 標頭檔案

          #ifndef WIDGET_H
          #define WIDGET_H
          
          #include <QWidget>
          #include <QSerialPort>
          
          namespace Ui {
          class Widget;
          }
          
          class Widget : public QWidget
          {
              Q_OBJECT
          
          public:
              explicit Widget(QWidget *parent = 0);
              ~Widget();
              // 宣告QSerialPort *serialPort
              QSerialPort *serialPort;
          
          private slots:
              void on_openBt_clicked();
          
              void on_closeBt_clicked();
          
              void on_sendBt_clicked();
              
              void on_clearBt_clicked();
              
          private:
              Ui::Widget *ui;
          };
          
          #endif // WIDGET_H
          
          
        • Cpp檔案

          #include "widget.h"
          #include "ui_widget.h"
          #include <QSerialPortInfo>
          #include <QMessageBox>
          
          Widget::Widget(QWidget *parent) :
              QWidget(parent),
              ui(new Ui::Widget)
          {
              ui->setupUi(this);
              // ...
          
              serialPort = new QSerialPort(this);
          
              // ... 
          }
          
          Widget::~Widget()
          {
              delete serialPort;
              delete ui;
          }
          
      • 開啟按鈕單擊訊號的槽函式

        // 點選開啟串列埠時將資料設定進串列埠並開啟串列埠
        void Widget::on_openBt_clicked()
        {
            QSerialPort::BaudRate baudReat;
            QSerialPort::DataBits dataBits;
            QSerialPort::StopBits stopBits;
            QSerialPort::Parity checkBits;
        
            // 獲取介面上的值
            switch(ui->baundrateCb->currentText().toInt()) {
            case QSerialPort::Baud4800:
                baudReat = QSerialPort::Baud4800;
                break;
            case QSerialPort::Baud9600:
                baudReat = QSerialPort::Baud9600;
                break;
            case QSerialPort::Baud115200:
                baudReat = QSerialPort::Baud115200;
                break;
            }
        
            switch(ui->dataCb->currentText().toInt()) {
            case QSerialPort::Data5:
                dataBits = QSerialPort::Data5;
                break;
            case QSerialPort::Data6:
                dataBits = QSerialPort::Data6;
                break;
            case QSerialPort::Data7:
                dataBits = QSerialPort::Data7;
                break;
            case QSerialPort::Data8:
                dataBits = QSerialPort::Data8;
                break;
            }
        
            int stopTmp = ui->stopCb->currentText().toInt();
            if (stopTmp == QSerialPort::OneStop) {
                stopBits = QSerialPort::OneStop;
            } else if (stopTmp == QSerialPort::OneAndHalfStop) {
                stopBits = QSerialPort::OneAndHalfStop;
            } else if (stopTmp == QSerialPort::TwoStop) {
                stopBits = QSerialPort::TwoStop;
            }
        
            if (ui->checkCb->currentText() == "none") {
                checkBits = QSerialPort::NoParity;
            }
        
            // 使用獲取到的資料設定串列埠
            serialPort->setPortName(ui->serialCb->currentText());
            serialPort->setBaudRate(baudReat);
            serialPort->setDataBits(dataBits);
            serialPort->setStopBits(stopBits);
            serialPort->setParity(checkBits);
        
            // 開啟串列埠,需要先判斷串列埠是否開啟成功
            if (serialPort->open(QIODevice::ReadWrite) == true) {
                QMessageBox::information(this, "提示", "success!");
            } else {
                QMessageBox::critical(this, "提示", "failed!");
            }
        }
        
      • 關閉按鈕單擊訊號槽函式

        void Widget::on_closeBt_clicked()
        {
            serialPort->close();
        }
        
      • 傳送按鈕單擊訊號槽函式

        void Widget::on_sendBt_clicked()
        {
            // 將UI傳送的QString轉換為char* 型別,寫入到serialPort
            serialPort->write(ui->sendEdit->text().toLocal8Bit().data());
        }
        
      • 串列埠有東西可讀時,在接收框中進行展示

        • 定義槽函式,在標頭檔案中

          #ifndef WIDGET_H
          #define WIDGET_H
          
          #include <QWidget>
          #include <QSerialPort>
          
          namespace Ui {
          class Widget;
          }
          
          class Widget : public QWidget
          {
              Q_OBJECT
          
          // ...
          
          private slots:
          	// ...
              // 定義串列埠有東西可讀時觸發的槽函式
              void serialPortReadyRead_Slot();
          
          // ...
          };
          
          #endif // WIDGET_H
          
        • 手動繫結可讀訊號與槽函式,在構造中

          Widget::Widget(QWidget *parent) :
              QWidget(parent),
              ui(new Ui::Widget)
          {
              ui->setupUi(this);
              QStringList serialNamePorts;
          
              serialPort = new QSerialPort(this);
          
              // 手動關聯讀訊號與自定義槽函式serialPortReadyRead_Slot(),串列埠有東西可讀時觸發槽函式
              connect(serialPort, SIGNAL(readyRead()), this, SLOT(serialPortReadyRead_Slot()));
          
          	// ...
          }
          
        • 實現槽函式

          // 串列埠有東西可讀時產生訊號,觸發槽函式
          void Widget::serialPortReadyRead_Slot()
          {
              // 接收UI中輸入框的資料
              QString buffer = QString(serialPort->readAll());
              // 將接收到的資料顯示到UI的recvEdit中
              ui->recvEdit->appendPlainText(buffer);
          
          }
          
      • 清除按鈕單擊訊號槽函式

        void Widget::on_clearBt_clicked()
        {
            ui->recvEdit->clear();
        }
        

4、程式打包和部署

  1. 切換到 Release 模式進行編譯

    由於缺少動態庫,打包好的程式暫時還無法執行

    • 匯出檔案位置:位於專案目錄所在路徑下,檔名以 Release 結尾,如:build-seial-Desktop_Qt_5_11_1_MinGW_32bit-Release

    • 圖示:

      打包

  2. 為打包好的程式更換圖示

    需要使用.ico格式的圖片

    • 將圖示複製到工程目錄下

    • 在工程檔案中新增如下程式碼,再重新編譯即可

      RC_ICONS = serial_icon.ico
      
  3. 封包操作,需要用到 Qt 的控制檯

    • 建立一個新的目錄,用於存放封包好的檔案,不能包含中文路徑

    • 將打包好的.exe檔案複製到新的目錄下

    • 從控制檯進入新目錄中cd /d C:\xxx\xxx

      D:\Tools\Qt\Qt5.11.1\5.11.1\mingw53_32>cd /d C:\Users\Dandelion\Desktop\SerialTools
      
      C:\Users\Dandelion\Desktop\SerialTools>dir
       驅動器 C 中的卷是 OS
       卷的序列號是 EAE6-1E0A
      
       C:\Users\Dandelion\Desktop\SerialTools 的目錄
      
      2023/03/10  02:22    <DIR>          .
      2023/03/10  02:20    <DIR>          ..
      2023/03/10  02:17            48,640 seial.exe
                     1 個檔案         48,640 位元組
                     2 個目錄 63,272,501,248 可用位元組
      
      C:\Users\Dandelion\Desktop\SerialTools>
      
    • 使用windeployqt工具將動態庫加到當前目錄下:windeployqt seial.exe

      C:\Users\Dandelion\Desktop\SerialTools>windeployqt seial.exe
      C:\Users\Dandelion\Desktop\SerialTools\seial.exe 32 bit, release executable
      Adding Qt5Svg for qsvgicon.dll
      Skipping plugin qtvirtualkeyboardplugin.dll due to disabled dependencies (Qt5Qml Qt5Quick).
      Direct dependencies: Qt5Core Qt5Gui Qt5SerialPort Qt5Widgets
      All dependencies   : Qt5Core Qt5Gui Qt5SerialPort Qt5Widgets
      To be deployed     : Qt5Core Qt5Gui Qt5SerialPort Qt5Svg Qt5Widgets
      Updating Qt5Core.dll.
      Updating Qt5Gui.dll.
      Updating Qt5SerialPort.dll.
      Updating Qt5Svg.dll.
      Updating Qt5Widgets.dll.
      Updating libGLESV2.dll.
      Updating libEGL.dll.
      Updating D3Dcompiler_47.dll.
      Updating opengl32sw.dll.
      Updating libgcc_s_dw2-1.dll.
      Updating libstdc++-6.dll.
      Updating libwinpthread-1.dll.
      Patching Qt5Core.dll...
      Creating directory C:/Users/Dandelion/Desktop/SerialTools/iconengines.
      Updating qsvgicon.dll.
      Creating directory C:/Users/Dandelion/Desktop/SerialTools/imageformats.
      Updating qgif.dll.
      Updating qicns.dll.
      Updating qico.dll.
      Updating qjpeg.dll.
      Updating qsvg.dll.
      Updating qtga.dll.
      Updating qtiff.dll.
      Updating qwbmp.dll.
      Updating qwebp.dll.
      Creating directory C:/Users/Dandelion/Desktop/SerialTools/platforms.
      Updating qwindows.dll.
      Creating directory C:/Users/Dandelion/Desktop/SerialTools/styles.
      Updating qwindowsvistastyle.dll.
      Creating C:\Users\Dandelion\Desktop\SerialTools\translations...
      Creating qt_ar.qm...
      Creating qt_bg.qm...
      Creating qt_ca.qm...
      Creating qt_cs.qm...
      Creating qt_da.qm...
      Creating qt_de.qm...
      Creating qt_en.qm...
      Creating qt_es.qm...
      Creating qt_fi.qm...
      Creating qt_fr.qm...
      Creating qt_gd.qm...
      Creating qt_he.qm...
      Creating qt_hu.qm...
      Creating qt_it.qm...
      Creating qt_ja.qm...
      Creating qt_ko.qm...
      Creating qt_lv.qm...
      Creating qt_pl.qm...
      Creating qt_ru.qm...
      Creating qt_sk.qm...
      Creating qt_uk.qm...
      
      C:\Users\Dandelion\Desktop\SerialTools>
      

相關文章