前言
本文只介绍Windows的QT上位机开发,以串口为例。本文的QT版本为5.15。
一、环境搭建
1.1 IDE下载
- Qt官方下载地址:https://www.qt.io/download#eval-form-modal
注意:这里下载的在线安装器,必须先下载然后根据提示注册,才能称为开源用户,否则为商业用户。
也可以使用国内服务器平台下载:
1.2 添加环境变量
安装教程可以百度,安装好之后将以下三个添加进来,需要重启才能生效
二、Qt Creator的使用
2.1 快捷键
快捷键 | 作用 |
---|---|
Ctrl + Space | 代码自动完成 |
Ctrl + I | 格式化文本 |
F2 | 跳转到下一个错误或警告 |
Shift + F2 | 跳转到上一个错误或警告 |
Ctrl + / | 注释或取消注释所选行 |
Ctrl + K | 跳转到定义 |
Ctrl + E | 打开最近的文件 |
Ctrl + Shift + E | 打开最近的项目 |
Ctrl + F12 | 显示当前文件的类或函数列表 |
F4 | 跳转到包含光标所在文本的定义 |
Ctrl + Shift + F4 | 返回上一个查看的位置 |
Ctrl + R | 构建项目。 |
Ctrl + B | 构建并运行项目。 |
Ctrl + D | 调试项目。 |
Ctrl + Shift + B | 构建所有项目。 |
Ctrl + T | 运行单元测试。 |
F5 | 重新运行应用程序。 |
F1 | 帮助文档 |
2.2 创建QT项目
创建基于窗口的Qt应用程序,然后输入项目名称与目录再下一步
选择构建脚本,这边选择qmake
这里默认就好,其中Base class是基类,有以下三个选项:
- QMainWindow:主窗口类,主窗口具有主菜单栏、工具栏和状态栏
- QWidget:是可视界面类的基类,也就是说QMainWindow类也是由QWidget继承封装而来。一般嵌入式里用
- QDialog:对话框类,建立一个对话框界面
直接下一步到构建套件Kit里,选择对应的编译器
最后直接完成就可以生成初始工程
项目文件介绍:
- 项目文件.pro:是项目管理文件,当加入了文件或者删除了文件,Qt Creator 会自动修改这个文件
- Header头文件分组:存放的是项目内所有的头文件*.h
- Source源文件分组:这个节点下存放的是项目内的所有 C++源码文件*.cpp。main是主函数,mainwindow.cpp是窗口界面对应的类的源文件
- Forms界面文件分组:这个节点下是存放项目内所有界面文件.ui。.ui文件由XML语言描述组成,编译时会生成相应的cpp文件,一般情况下不需要在xml格式下对这个文件进行编辑
双击mainwindow.ui,进入“Ui 设计器”页面,拖动Label控件,就可以输入简单文本,这里我们已经完成显示“Hello World!”的设计了。
回到编辑界面,点击编译,就可以生成一个简单UI
2.3 帮助文档使用
这边以QPushButton
为例,在帮助里搜索对应的类
可以查看Properties和Public Functions这个标题下的属性和函数,点击Inherits可以查看父类的信息
最后可以使用快捷键Alt + ←
返回到上一级,或者Alt + →
进入下一级
三、Qt信号与槽
- 信号(Signal):就是在特定情况下被发射的事件,例如 PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号,简而言之就是输入激励
- 槽(Slot):就是对信号响应的函数,职责就是对Qt框架中产生的信号进行处理
3.1 标准信号与槽的使用
标准信号与槽就是指QT自带信号与槽,例如PushButton
最常见的信号就是鼠标单击时发射的 clicked() 信号。信号与槽关联是用QObject::connect()
函数实现的,connect()是QObject类的一个静态函数,而QObject是所有Qt类的基类,通过帮助可以查看函数使用
功能简单只需要修改main.cpp文件
#include <QApplication>
#include <QPushButton>
#include <QLineEdit>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//点击按钮
QPushButton *button = new QPushButton("Click Me");
QObject::connect(button, &QPushButton::clicked, [=]() {
qInfo() << "Button Clicked!";
});
button->show();
return a.exec();
}
效果如下,敲击按钮可以打印输出
3.2 自定义信号与槽
自定义信号可以更好的对控件进行统一管理
- 创建信号与槽
新建一个不带ui的项目(取消勾选ui),主函数文件main.cpp可以不动,在mainwindow.h头文件里添加函数声明,有以下注意点:
- 在使用信号与槽的类中,必须在类的定义中加入宏Q_OBJECT
- 信号需要使用
signals
关键字进行声明 - 声明的槽函数必须是无返回值类型 void,和无需参数
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
/* 声明一个信号,只需声明,无需定义 */
void pushButtonTextChanged();
public slots:
/* 声明一个槽函数 */
void changeButtonText();
/* 声明按钮点击的槽函数 */
void pushButtonClicked();
private:
/* 声明一个对象pushButton */
QPushButton *pushButton;
};
#endif // MAINWINDOW_H
- 信号与槽的实现
在mianwindow.cpp里实现槽函数的定义,有以下注意点:
- 使用
connect()
连接信号与槽,connect()是QObject类的一个静态函数,而QObject是所有Qt类的基类,在实际调用时可以忽略前面的限定符 - 习惯性在信号函数前加关键字:
emit
表示显示的声明一下信号要被发射了,可以省略不写,没有特殊含义 - 因为我们pushButtonClicked()是本类里定义的槽,所以用this来接收
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/* 设置窗体的宽为800,高为480 */
this->resize(800,480);
/* 实例化pushButton对象 */
pushButton = new QPushButton(this);
/* 调用setText()方法设定按钮的文本 */
pushButton->setText("我是一个按钮");
/* 信号与槽连接 */
connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));
}
MainWindow::~MainWindow() {}
/* 实现按钮点击槽函数 */
void MainWindow::pushButtonClicked()
{
/* 使用emit发送信号 */
emit pushButtonTextChanged();
}
/* 实现按钮文本改变的槽函数 */
void MainWindow::changeButtonText()
{
/* 在槽函数里改变按钮的文本 */
pushButton->setText("被点击了!");
}
效果就是被点击后切换按钮文本
四、Qt控件
4.1 QMainWindow
QMainWindow
提供了用于构建应用程序用户界面的框架:
- 菜单栏(Menu Bar):
QMenuBar
对象用于创建菜单栏,菜单栏通常包含一个或多个菜单,每个菜单包含若干个菜单项。 - 工具栏(Tool Bar):
QToolBar
对象用于创建工具栏,工具栏通常包含一组快捷操作按钮,用于执行常用的功能。 - 状态栏(Status Bar):
QStatusBar
对象用于创建状态栏,用于显示应用程序的状态信息、提示信息等。 - 中央部件(Central Widget): 通常是一个自定义的QWidget派生类,作为主窗口的中央区域,用于放置应用程序的主要内容。centralWidget支持支持文档-视图架构,可以通过多文档界面(MDI)或单文档界面(SDI)的方式管理多个文档。
- Dock窗口(Dock Widgets):
QDockWidget
对象用于创建可停靠的面板,用户可以拖动和停靠这些面板。
- 创建菜单栏
首先在最上方添加菜单项,然后新建菜单列表
将菜单列表托上去就可以添加,直接添加只能添加英文
- 创建工具栏
右击添加工具栏,然后可以使用同样的方式直接拖动
- 状态栏
状态栏是一个用于显示应用程序状态信息、提示信息和用户交互的底部区域。状态栏通常用于向用户提供反馈、进度信息或一般性的应用程序状态,可以使用statusBar()
函数来获取当前的状态栏对象
QLabel *statusLabel = new QLabel("Status Label", this);
statusBar()->addPermanentWidget(statusLabel);
- 停靠窗口
拖拽控件到ui里,右侧可以看到相应的对象与类,选中后在右下角属性对其修改
4.2 按钮
首先打开ui设计界面,可以从左侧过滤器里找到Button
QPushButton
:下压按钮,继承QAbstractButton类,通常用于执行命令或触发事件,上一小节使用过QToolButton
:工具按钮,继承QAbstractButton类。是一种用于命令或者选项的可以快速访问的按钮,通常在ToolBar里面。工具按钮通常显示的是图标,而不是文本标签。ToolButton支持自动浮起。在自动浮起模式中,按钮只有在鼠标指向它的时候才绘制三维的框架。QRadioButton
:选择按钮,继承QAbstractButton类。RadioButton 单选按钮(单选框)通常成组出现,用于提供两个或多个互斥选项。QCheckBox
:检查框,继承QAbstractButton。复选按钮(复选框)与 RadioButton 的区别是选择模式,单选按钮提供多选一,复选按钮提供多选多。.QCommandLinkButton
:命令链接按钮,继承QPushButton。QCommandLinkButton 控件和 RadioButton 相似,都是用于在互斥选项中选择一项。表面上同平面按钮一样,但是 CommandLinkButton 除带有正常的按钮上的文字描述文本外,默认情况下,它也将携带一个箭头图标,表明按下按钮将打开另一个窗口或页面。QDialogButtonBox
:对话框按钮,继承QWidget。常用于对话框里自定义按钮,比如“确定”和“取消”按钮。
4.3 容器
- Group Box(组框):
QGroupBox
继承 QWidget,为构建分组框提供了支持。分组框通常带有一个边框和一个标题栏,作为容器部件来使用,在其中可以布置各种窗口部件。布局时可用作一组控件的容器,但是需要注意的是,内部通常使用布局控件(如 QBoxLayout)进行布局。组框还提供键盘快捷方式,键盘快捷方式将键盘焦点移动到组框的一个子部件。 - Scroll Area(滚动区域):
QScrollArea
继承QAbstractScrollArea。滚动区域用于在框架中显示子部件的内容。如果小部件超过框架的大小,视图就会出现滚动条,以便可以查看子小部件的整个区域。 - Tool Box(工具箱):
QToolBox
继承QFrame。QToolBox 类提供了一列选项卡小部件项。工具箱是一个小部件,它显示一列选项卡在另一列的上面,当前项显示在当前选项卡的下面。每个选项卡在选项卡列中都有一个索引位置。选项卡的项是 QWidget。 - Tab Widget(标签小部件):
QTabWidget
继承 QWidget。abWidget 类提供了一组选项卡(多页面)小部件。QTabWidget主要是用来分页显示的,每一页一个界面,众多界面公用一块区域,节省了界面大小,很方便的为用户显示更多的信息。 - Stacked WIdget(堆叠小部件):
QStackedWidget
继承 QFrame。QStackedWidget 类提供了一个小部件堆栈,其中一次只能看到一个小部件,与 QQ 的设置面板类似。QStackedWidget 可用于创建类似于 QTabWidget 提供的用户界面。它是构建在 QStackedLayout 类之上的一个方便的布局小部件。 - Frame(帧):
QFrame
继承了QWidget的属性, QFrame 类是有框架的窗口部件的基类,它绘制框架并且调用一个虚函数 drawContents()来填充这个框架。 这种类型的容器窗口可以提供边框, 并且可以设置边框的样式、宽度以及边框的阴影 - Widget(小部件):
QWidget
类是所有用户界面对象的基类(如 QLabel 类继承于 QFrame 类,而 QFrame 类又继承于 QWidget 类)。 - MDI Area(MDI 区域):
QMdiArea
继承QAbstractScrollArea。QMdiArea 小部件提供一个显示 MDI 窗口的区域。QMdiArea的功能本质上类似于MDI窗口的窗口管理器。大多数复杂的程序,都使用MDI框架 - Dock Widget(停靠窗体部件):
QDockWidget
继承QWidget。QDockWidget 类提供了一个小部件,可以停靠在 QMainWindow 内,也可以作为桌面的顶级窗口浮动。
4.4 窗口
可以看出窗口部件很多,有输入与显示窗口,这边就挑部分介绍
- Comb Box(组合框):
QComboBox
继承 QWidget 类,被 QFontComboBox 类继承。通常用于用户显示选项列表的方法,这种方法占用最少的屏幕空间。 - Label(标签):QLabel提供了一种用于文本或图像显示的小部件
- Progress Bar(进度条):
QProgressBar
继承 QWidget。QProgressBar 小部件提供了一个水平或垂直的进度条。进度条用于向用户显示操作的进度,并向他们确认应用程序仍在运行。 - Line Edit(单行编辑框):
QLineEdit
继承QWidget。QLineEdit 小部件是一个单行文本编辑器。行编辑允许用户使用一组有用的编辑函数输入和编辑一行纯文本,包括撤消和重做、剪切和粘贴以及拖放。 - Text Edit(文本编辑框):
QTextEdit
继承QAbstractScrollArea,被 QTextBrowser 继承。QTextEdit 是一个高级所见即所得查看器/编辑器,支持使用 html 样式的标记进行 rich text 格式化。 - Spin Box(数字旋转框):
QSpinBox
继承 QAbstractSpinBox。用于处理整数和离散值(例如:月份名称)而 QDoubleSpinBox 则用于处理浮点值。他们之间的区别就是处理数据的类型不同,其他功能都基本相同。 - Time Edit(时间编辑框):
QTimeEdit
继承 QDateTimeEdit。QTimeEdit 用于编辑时间,而 QDateEdit 用于编辑日期。 - Date/Time Edit(日期时间编辑框):
QDateTimeEdit
提供了一个用于编辑日期和时间的小部件。QDateTimeEdit 允许用户使用键盘或箭头键编辑日期,以增加或减少日期和时间值。 - List Widget(清单控件):
QListWidget
继承 QListView。QListWidget 类提供了一个基于项的列表小部件 - QTreeWidget(树形列表框):
QTreeWidget
继承 QTreeView。QTreeWidget 类提供了一个使用预定义树模型的树视图。
4.5 布局管理
基本布局管理类包括:QBoxLayout、QGridLayout、QFormLayout 和 QStackedLayout。这些类都从 QLayout 继承而来,它们都来源于 QObject(而不是 QWidget)
QBoxLayout
提供了水平和垂直的布局管理;QFormLayout
提供了将输入部件和标签成组排列的布局管理;QGridLayout
提供了网格形式的布局管理;QStackedLayout
提供了一组布局后的部件,可以对它们进行分布显示。
Layouts组里面的 4 种布局,如下:
效果如下:
五、SerialPort
这边新建一个名为SerialPort的工程
5.1 修改.Pro文件
使用串口,需要在.pro项目文件中添加serialport
串口模块的支持
QT += core gui serialport
5.2 修改mainwindow.h头文件
头文件需要包含串口常用的两个类QSerialPortInfo
和QSerialPort
#include <QSerialPort>
#include <QSerialPortInfo>
- QSerialPortInfo:提供有关现有串行端口的信息
- QSerialPort:提供访问串行端口的功能
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QPushButton>
#include <QTextBrowser>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QLabel>
#include <QComboBox>
#include <QGridLayout>
#include <QMessageBox>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
/* 串口对象 */
QSerialPort *serialPort;
/* 用作接收数据 */
QTextBrowser *textBrowser;
/* 用作发送数据 */
QTextEdit *textEdit;
/* 按钮 */
QPushButton *pushButton[2];
/* 下拉选择盒子 */
QComboBox *comboBox[5];
/* 标签 */
QLabel *label[5];
/* 垂直布局 */
QVBoxLayout *vboxLayout;
/* 网络布局 */
QGridLayout *gridLayout;
/* 主布局 */
QWidget *mainWidget;
/* 设置功能区域 */
QWidget *funcWidget;
/* 布局初始化 */
void layoutInit();
/* 扫描系统可用串口 */
void scanSerialPort();
/* 波特率项初始化 */
void baudRateItemInit();
/* 数据位项初始化 */
void dataBitsItemInit();
/* 检验位项初始化 */
void parityItemInit();
/* 停止位项初始化 */
void stopBitsItemInit();
private slots:
void sendPushButtonClicked();
void openSerialPortPushButtonClicked();
void serialPortReadyRead();
};
#endif // MAINWINDOW_H
5.3 修改mainwindow.cpp源文件
QSerialPortInfo
使用静态函数生成 QSerialPortInfo 对象的列表。列表中的每个 QSerialPortInfo 对象代表一个串行端口,可以查询该端口的名称,系统位置,描述和制造商。QSerialPortInfo类也可以被用作setPort()方法的输入参数,这些参数限定串口端口号,波特率等。
对于QSerialPort
类,我们可以使用如下方法来设置串口的相关参数。
setBaudRate()
设置波特率setDataBits()
设置数据位setParity()
设置校验位setStopBits()
设置停止位setFlowControl()
设置流控制
设置端口后,可以使用open()
方法以只读 (r/o)、仅写 (w/o) 或可读可写 (r/w) 模式打开它。成功打开后,可以使用close()
方法关闭端口并取消 I/O 操作。一旦知道端口已准备好读取或写入,就可以使用read()
或write()
方法。
#include "mainwindow.h"
#include <QGuiApplication>
#include <QScreen>
#include <QRect>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/* 布局初始化 */
layoutInit();
/* 扫描系统的串口 */
scanSerialPort();
/* 波特率项初始化 */
baudRateItemInit();
/* 数据位项初始化 */
dataBitsItemInit();
/* 检验位项初始化 */
parityItemInit();
/* 停止位项初始化 */
stopBitsItemInit();
}
void MainWindow::layoutInit()
{
/* 获取屏幕的分辨率,Qt官方建议使用这
* 种方法获取屏幕分辨率,防上多屏设备导致对应不上
* 注意,这是获取整个桌面系统的分辨率
*/
QList <QScreen *> list_screen = QGuiApplication::screens();
/* 如果是ARM平台,
直接设置大小为屏幕的大小 */
#if __arm__
/* 重设大小 */
this->resize(list_screen.at(0)->geometry().width(),
list_screen.at(0)->geometry().height());
#else
/* 否则则设置主窗体大小为800x480 */
this->resize(800, 480);
#endif
/* 初始化 */
serialPort = new QSerialPort(this);
textBrowser = new QTextBrowser();
textEdit = new QTextEdit();
vboxLayout = new QVBoxLayout();
funcWidget = new QWidget();
mainWidget = new QWidget();
gridLayout = new QGridLayout();
/* QList链表,字符串类型 */
QList <QString> list1;
list1<<"串口号:"<<"波特率:"<<"数据位:"<<"检验位:"<<"停止位:";
for (int i = 0; i < 5; i++) {
label[i] = new QLabel(list1[i]);
/* 设置最小宽度与高度 */
label[i]->setMinimumSize(80, 30);
/* 自动调整label的大小 */
label[i]->setSizePolicy(
QSizePolicy::Expanding,
QSizePolicy::Expanding
);
/* 将label[i]添加至网格的坐标(0, i) */
gridLayout->addWidget(label[i], 0, i);
}
for (int i = 0; i < 5; i++) {
comboBox[i] = new QComboBox();
comboBox[i]->setMinimumSize(80, 30);
/* 自动调整label的大小 */
comboBox[i]->setSizePolicy(
QSizePolicy::Expanding,
QSizePolicy::Expanding
);
/* 将comboBox[i]添加至网格的坐标(1, i) */
gridLayout->addWidget(comboBox[i], 1, i);
}
/* QList链表,字符串类型 */
QList <QString> list2;
list2<<"发送"<<"打开串口";
for (int i = 0; i < 2; i++) {
pushButton[i] = new QPushButton(list2[i]);
pushButton[i]->setMinimumSize(80, 30);
/* 自动调整label的大小 */
pushButton[i]->setSizePolicy(
QSizePolicy::Expanding,
QSizePolicy::Expanding
);
/* 将pushButton[0]添加至网格的坐标(i, 5) */
gridLayout->addWidget(pushButton[i], i, 5);
}
pushButton[0]->setEnabled(false);
/* 布局 */
vboxLayout->addWidget(textBrowser);
vboxLayout->addWidget(textEdit);
funcWidget->setLayout(gridLayout);
vboxLayout->addWidget(funcWidget);
mainWidget->setLayout(vboxLayout);
this->setCentralWidget(mainWidget);
/* 占位文本 */
textBrowser->setPlaceholderText("接收到的消息");
textEdit->setText("myQt");
/* 信号槽连接 */
connect(pushButton[0], SIGNAL(clicked()),
this, SLOT(sendPushButtonClicked()));
connect(pushButton[1], SIGNAL(clicked()),
this, SLOT(openSerialPortPushButtonClicked()));
connect(serialPort, SIGNAL(readyRead()),
this, SLOT(serialPortReadyRead()));
}
void MainWindow::scanSerialPort()
{
/* 查找可用串口 */
foreach (const QSerialPortInfo &info,
QSerialPortInfo::availablePorts()) {
comboBox[0]->addItem(info.portName());
}
}
void MainWindow::baudRateItemInit()
{
/* QList链表,字符串类型 */
QList <QString> list;
list<<"1200"<<"2400"<<"4800"<<"9600"
<<"19200"<<"38400"<<"57600"
<<"115200"<<"230400"<<"460800"
<<"921600";
for (int i = 0; i < 11; i++) {
comboBox[1]->addItem(list[i]);
}
comboBox[1]->setCurrentIndex(7);
}
void MainWindow::dataBitsItemInit()
{
/* QList链表,字符串类型 */
QList <QString> list;
list<<"5"<<"6"<<"7"<<"8";
for (int i = 0; i < 4; i++) {
comboBox[2]->addItem(list[i]);
}
comboBox[2]->setCurrentIndex(3);
}
void MainWindow::parityItemInit()
{
/* QList链表,字符串类型 */
QList <QString> list;
list<<"None"<<"Even"<<"Odd"<<"Space"<<"Mark";
for (int i = 0; i < 5; i++) {
comboBox[3]->addItem(list[i]);
}
comboBox[3]->setCurrentIndex(0);
}
void MainWindow::stopBitsItemInit()
{
/* QList链表,字符串类型 */
QList <QString> list;
list<<"1"<<"2";
for (int i = 0; i < 2; i++) {
comboBox[4]->addItem(list[i]);
}
comboBox[4]->setCurrentIndex(0);
}
void MainWindow::sendPushButtonClicked()
{
/* 获取textEdit数据,转换成utf8格式的字节流 */
QByteArray data = textEdit->toPlainText().toUtf8();
serialPort->write(data);
}
void MainWindow::openSerialPortPushButtonClicked()
{
if (pushButton[1]->text() == "打开串口") {
/* 设置串口名 */
serialPort->setPortName(comboBox[0]->currentText());
/* 设置波特率 */
serialPort->setBaudRate(comboBox[1]->currentText().toInt());
/* 设置数据位数 */
switch (comboBox[2]->currentText().toInt()) {
case 5:
serialPort->setDataBits(QSerialPort::Data5);
break;
case 6:
serialPort->setDataBits(QSerialPort::Data6);
break;
case 7:
serialPort->setDataBits(QSerialPort::Data7);
break;
case 8:
serialPort->setDataBits(QSerialPort::Data8);
break;
default: break;
}
/* 设置奇偶校验 */
switch (comboBox[3]->currentIndex()) {
case 0:
serialPort->setParity(QSerialPort::NoParity);
break;
case 1:
serialPort->setParity(QSerialPort::EvenParity);
break;
case 2:
serialPort->setParity(QSerialPort::OddParity);
break;
case 3:
serialPort->setParity(QSerialPort::SpaceParity);
break;
case 4:
serialPort->setParity(QSerialPort::MarkParity);
break;
default: break;
}
/* 设置停止位 */
switch (comboBox[4]->currentText().toInt()) {
case 1:
serialPort->setStopBits(QSerialPort::OneStop);
break;
case 2:
serialPort->setStopBits(QSerialPort::TwoStop);
break;
default: break;
}
/* 设置流控制 */
serialPort->setFlowControl(QSerialPort::NoFlowControl);
if (!serialPort->open(QIODevice::ReadWrite))
QMessageBox::about(NULL, "错误",
"串口无法打开!可能串口已经被占用!");
else {
for (int i = 0; i < 5; i++)
comboBox[i]->setEnabled(false);
pushButton[1]->setText("关闭串口");
pushButton[0]->setEnabled(true);
}
} else {
serialPort->close();
for (int i = 0; i < 5; i++)
comboBox[i]->setEnabled(true);
pushButton[1]->setText("打开串口");
pushButton[0]->setEnabled(false);
}
}
void MainWindow::serialPortReadyRead()
{
/* 接收缓冲区中读取数据 */
QByteArray buf = serialPort->readAll();
textBrowser->insertPlainText(QString(buf));
}
MainWindow::~MainWindow()
{
}
layoutInit()
界面布局初始化设置,根据实际的屏的大小,设置全屏显示。其中我们用到垂直布局和网格布局scanSerialPort()
查找系统可用的串口,并添加串口名到 comboBox[0]中。baudRateItemInit()
波特率初始化,预设常用的波特率,115200 作为默认选项。并添加波特率到comboBox[1]中。dataBitsItemInit()
数据位项初始化,设置默认数据位为 8。parityItemInit()
校验位项初始化,默认无校验位。stopBitsItemInit()
停止位项初始化,默认停止位为 1。sendPushButtonClicked()
发送数据,点击发送按钮时触发。openSerialPortPushButtonClicked()
打开或者关闭串口。以我们设置的项使用 Qt 串口提供的设置串口的方法如 setDataBits(QSerialPort::DataBits)等,按步骤设置完串口需要配置的参数就可以打开或者关闭串口了。serialPortReadyRead()
从缓冲区里读出数据,并显示到 textBrowser 里。
5.4 效果
六、QT程序发布与打包
6.1 添加图标
找一个.ico文件,图标下载网址如:https://www.iconfont.cn/
然后在.pro文件中添加如下一行代码:
RC_ICONS = SerialPort.ico
6.2 生成Release版程序
如果要发布程序需要切换为Release版本(Debug为调试版本),编译器会对生成的Release版可执行程序进行优化,生成的可执行程序会更小。
在Release里找到SerialPort.exe,双击发现打不开,报错原因是找不到一些相关的动态库
6.3 添加动态库
Qt官方给我们提供了相关的发布工具,这个工具叫做windeployqt.exe,文件目录在安装路径的bin文件夹里,如我这边是C:\Qt\5.15.0\mingw81_32\bin
先新建一个文件夹,将SerialPort.exe文件复制进来。如果安装时配好环境变量,可以直接打开CMD或者shell,这边我们选择Powershell
在当前目录下执行以下命令进行打包
windeployqt SerialPort.exe
此时再双击SerialPort.exe就可以运行
注意:环境变量里bin路径配置要与新建Qt工程选择的编译器版本相同,否则无法运行。如一个是64位一个为32位
6.4 Qt程序打包
6.4.1 打包成单文件版
上面的绿色便携版的不便之处是,发送给别人的时候还需要先压缩一下,别人接受后还需要先解压再使用,就很不方便。因此单文件版就应运而生,给别人发送的是一个包含运行环境的exe文件,双击即可使用。
使用Enigma Virtual Box,官网:https://www.enigmaprotector.com/en/downloads.html
先选择要打包的文件
新建递归文件夹,然后把上一小节整个文件夹拖进来
然后开始压缩文件
然后就可以生成一个可以运行的文件SerialPort_boxed.exe
6.4.2 打包成可安装版本
将应用程序和相关的动态库打包成安装包的工具有很多,拿Inno Setup使用举例,官网:https://jrsoftware.org/isdl.php#stable
新建直接Next
填写相关信息
设置安装文件夹
添加应用程序与依赖文件夹
Next到这里,是应用程序文档信息,一般空着就行。
继续到选择安装语言,这里可以去inno setup官网找到translations,点击然后下拉找到简体中文语言包Chinese (Simplified)下载下来。然后找到Inno Setup安装文件,进入Languages子文件夹,将语言包复制进去就行。
设置安装包信息
继续到执行脚本
双击生成文件可以安装