Qt開発ノート - シリアルポートプログラミングとWindowsアプリケーションのビルド手順

目次- 1、プロジェクト作成

  • 2、ユーザーインターフェース構築
  • 3、機能実装
  • 4、アプリケーションのパッケージ化と配布

1、プロジェクト作成

シリアル通信ツールの開発

  • Qt Widgets Applicationプロジェクト「serial」を作成
  • ベースクラスはWidgetを選択

2、ユーザーインターフェース構築

  • UI設計

    1. 受信表示領域として、Input WidgetsカテゴリのQPlainTextEditコンポーネントを使用。編集可能に設定し、読み取り専用プロパティを有効にする
    2. ボーレート選択用のQComboBoxコンポーネントを追加
    3. 送信入力フィールドとしてQLineEditコンポーネントを配置
    4. 情報表示領域として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>デモ情報...</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に表示する

    MCUとの接続が必要です

    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
        QStringList portNames;
    
        // QSerialPortInfo::availablePorts()で利用可能なシリアルポートを自動検索
        foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
            portNames << info.portName();
        }
    
        // ポートリストをコンボボックスに追加
        ui->serialCb->addItems(portNames);
    }
    
    
  3. 各UIコントロールの機能実装

    • シリアルポートオープン時の初期化処理

      • シリアルポートの宣言と生成

        • ヘッダファイル

          #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;
          
          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
          
          
        • ソースファイル

          #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 baudRate;
            QSerialPort::DataBits dataBits;
            QSerialPort::StopBits stopBits;
            QSerialPort::Parity parityBits;
        
            // UIから設定値を取得
            switch(ui->baundrateCb->currentText().toInt()) {
            case QSerialPort::Baud4800:
                baudRate = QSerialPort::Baud4800;
                break;
            case QSerialPort::Baud9600:
                baudRate = QSerialPort::Baud9600;
                break;
            case QSerialPort::Baud115200:
                baudRate = 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") {
                parityBits = QSerialPort::NoParity;
            }
        
            // シリアルポートに設定を適用
            serialPort->setPortName(ui->serialCb->currentText());
            serialPort->setBaudRate(baudRate);
            serialPort->setDataBits(dataBits);
            serialPort->setStopBits(stopBits);
            serialPort->setParity(parityBits);
        
            // シリアルポートを開く
            if (serialPort->open(QIODevice::ReadWrite) == true) {
                QMessageBox::information(this, "通知", "成功!");
            } else {
                QMessageBox::critical(this, "通知", "失敗!");
            }
        }
        
        
      • クローズボタンクリック時の処理

        void Widget::on_closeBt_clicked()
        {
            serialPort->close();
        }
        
        
      • 送信ボタンクリック時の処理

        void Widget::on_sendBt_clicked()
        {
            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 portNames;
          
              serialPort = new QSerialPort(this);
          
              connect(serialPort, SIGNAL(readyRead()), this, SLOT(serialPortReadyRead_Slot()));
          
          	// ...
          }
          
          
        • スロット関数の実装

          void Widget::serialPortReadyRead_Slot()
          {
              QString buffer = QString(serialPort->readAll());
              ui->recvEdit->appendPlainText(buffer);
          }
          
          
      • クリアボタンクリック時の処理

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

4、アプリケーションのパッケージ化と配布

  1. Releaseモードでビルドを行う

    DLLが不足しているため、実行はできません

    • 出力先:プロジェクトフォルダ内、Release拡張子付きのフォルダ
    • 例:build-serial-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 serial.exe
                     1 個のファイル         48,640 バイト
                     2 個のディレクトリ 63,272,501,248 バイトの空き領域
      
      C:\Users\Dandelion\Desktop\SerialTools>
      
      
    • windeployqtを使用して必要なDLLを追加:windeployqt serial.exe

      C:\Users\Dandelion\Desktop\SerialTools>windeployqt serial.exe
      C:\Users\Dandelion\Desktop\SerialTools\serial.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>
      
      

タグ: Qt シリアルポート Windowsアプリケーション QtWidgets SerialPort

5月26日 19:09 投稿