Qt 学习笔记 - 第三章 - Qt的三驾马车之一 - 串口编程 + 程序打包成Windows软件
Qt 学习笔记全系列传送门:
1、创建项目
实现串口助手
-
创建 Qt Widgets Application 项目 seial
-
基类选择 Widget
2、UI
-
UI设计
- 接收框组件,在分类 Input Widgets 中,Plain Text Edit 组件(QPlainTextEdit),双击可以编辑选项,置顶项为默认选择属性,勾选组件的只读属性 readOnly
- 属性选择,在分类 Input Widgets 中,Combo Box 组件(QComboBox)
- 发送框,在分类在 Input Widgets 中,Line Edit 组件(QLineEdit)
- 信息框,写个广告之类的可以使用,在分类 中,Group Box 组件(QGroupBox)
- 控件改名
-
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、逻辑功能
-
在工程文件中引入
serialport
QT += core gui serialport
-
获取串口信息并展示到页面上,在目前 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); }
-
其他控件的逻辑功能
-
点击打开串口时对串口进行初始化
-
对串口的声明和创建
-
头文件
#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、程序打包和部署
-
切换到 Release 模式进行编译
由于缺少动态库,打包好的程序暂时还无法运行
-
导出文件位置:位于项目目录所在路径下,文件名以 Release 结尾,如:
build-seial-Desktop_Qt_5_11_1_MinGW_32bit-Release
-
图示:
-
-
为打包好的程序更换图标
需要使用
.ico
格式的图片-
将图标拷贝到工程目录下
-
在工程文件中添加如下代码,再重新编译即可
RC_ICONS = serial_icon.ico
-
-
封包操作,需要用到 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>
-