Bootstrap

QT上位机开发


前言

  本文只介绍Windows的QT上位机开发,以串口为例。本文的QT版本为5.15。

一、环境搭建

1.1 IDE下载

  也可以使用国内服务器平台下载:

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为例,在帮助里搜索对应的类
在这里插入图片描述
  可以查看PropertiesPublic 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 自定义信号与槽

  自定义信号可以更好的对控件进行统一管理

  1. 创建信号与槽
      新建一个不带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
  1. 信号与槽的实现
      在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对象用于创建可停靠的面板,用户可以拖动和停靠这些面板。

在这里插入图片描述

  1. 创建菜单栏
      首先在最上方添加菜单项,然后新建菜单列表
    在这里插入图片描述
      将菜单列表托上去就可以添加,直接添加只能添加英文
    在这里插入图片描述
  2. 创建工具栏
      右击添加工具栏,然后可以使用同样的方式直接拖动
    在这里插入图片描述
  3. 状态栏
      状态栏是一个用于显示应用程序状态信息、提示信息和用户交互的底部区域。状态栏通常用于向用户提供反馈、进度信息或一般性的应用程序状态,可以使用statusBar()函数来获取当前的状态栏对象
QLabel *statusLabel = new QLabel("Status Label", this);
statusBar()->addPermanentWidget(statusLabel);
  1. 停靠窗口
      拖拽控件到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头文件

  头文件需要包含串口常用的两个类QSerialPortInfoQSerialPort

#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子文件夹,将语言包复制进去就行。
在这里插入图片描述
  设置安装包信息
在这里插入图片描述
  继续到执行脚本
在这里插入图片描述
  双击生成文件可以安装
在这里插入图片描述

;