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

来源:https://www.cnblogs.com/dandelion-000-blog/archive/2023/03/10/17202096.html
-Advertisement-
Play Games

Qt 學習筆記全系列傳送門: Qt 學習筆記 - 第一章 - 快速開始、信號與槽 Qt 學習筆記 - 第二章 - 添加圖片、佈局、界面切換 【本章】Qt 學習筆記 - 第三章 - Qt的三駕馬車之一 - 串口編程 + 程式打包成Windows軟體 1、創建項目 實現串口助手 創建 Qt Widget ...


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>
      

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一般來說,項目由子模塊組成,拿到後端提供過來的介面,一般也是按照子模塊來分類提供的.請教一下各位,你們前端項目是如何管理api的? 希望各位貼點你們的優秀代碼段上來學習學習. 常見: 各個模塊的api存放到單獨的js文件里,返回一個請求實例promise對象 使用的時候根據需求引入相應的請求方法 / ...
  • 其他章節請看: webgl 系列 變換矩陣和動畫 動畫就是不停地將某個東西變換(transform)。例如將三角形不停地旋轉就是一個動畫 和 CSS transform 類似,變換有三種形式:平移、縮放和旋轉。 簡單的變換用普通表達式容易實現,如果事情複雜,比如旋轉後平移,這時就可以使用變換矩陣。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 網站效果演示:ashuai.work:8888/#/myLoad GitHub倉庫地址代碼:github.com/shuirongshu… 載入中思路分析 實現載入中效果,一般有兩種方式: 第一種是:搞一個load組件,然後使用Vue.e ...
  • <!-- * @description 表格組件 * @fileName TableList.vue * @authorQ * @date 2021/05/15 15:13:45 --> <template> <div class="table-container"> <el-table v-if= ...
  • 定義 使用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。原型模式是一種對象創建型模式 百科。 通俗的說就是原型模式是一種創建型設計模式,指定某個對象(通過某種方式)得到一個新的對象,在記憶體中擁有新的地址,得到的對象與原對象是是相互獨立的,即得到跟原對象一樣的對象 當我們需要兩個一模一 ...
  • 前端中介者模式(Mediator Pattern),用於將對象之間的通信解耦並集中管理。它通過引入一個中介者對象,將對象之間的交互轉移到中介者對象中,從而避免對象之間直接相互通信。 在前端開發中,中介者模式常常被用於管理複雜的用戶界面或組件之間的交互,比如 GUI 組件、聊天室、游戲等等。通過引入一 ...
  • 這篇文章主要描述如何解決消息重發的問題,目前主流的消息隊列產品都採用了At least once的服務質量,這就導致了很難避免消息重發的情況,我們可以將消費者業務邏輯設計成冪等服務來解決消息重發問題。 ...
  • 1. 減少記憶體使用 1.1. 減少堆記憶體的使用 1.1.1. 使用更少的記憶體意味著堆被填滿的頻率會降低,需要的GC周期會更少,其效果也可以成倍增強 1.1.2. 更少的新生代回收意味著對象的晉升年齡增加的頻率降低 1.1.3. 對象晉升到老年代的可能性也降低了 1.1.4. Full GC周期(或者 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...