极速入门体验Qt5软件开发,从安装到打包,少走弯路,Qt入门指南,串口调试助手开发实战

2023-11-19


前言

C语言学了这么久是否还跟我一样对着黑窗口体验与printf的极致交流,学了Python后似乎忘了C/C++也能开发图形化界面,直到揭开Qt神秘的面纱,才有相见恨晚的感觉。在此之前,都用Python开发上位机软件,语言简洁精炼,入门简单高效。但世界总是公平的,有舍就有的,有好处必然有坏处。其一,多次碰到程序卡顿的情况。其二,使用串口时若没有在退出前关闭,就会一直占用。其三,打包出来的可执行程序体积太大。以上三宗罪并不是不可解决,但纠其根本,在于脚本语言本身没有低级语言的稳定性和速度。个人开发者倒是无所谓,对于工程实践更多需要是稳定和流畅。

难学的东西学会了肯定是有好处的,于是开始探索C/C++开发图像化界面的道路。了解了MFC,是微软公司提供的一个类库,只针对Windows平台开发可视化界面,许多windows程序都是由它开发,入门了一两天,用起来还是挺痛苦的。知道被Qt吸引,以其跨平台,文档丰富而完善,入门轻松高效,界面优美大方等优势,无可挑剔。

不过对于初学者,C语言学得多,C++部分看得懂,学起来还是有一些门槛的。本文基于三天入门过程中走过的弯路,以先成功后学习的理念,总结出一篇完整开发串口调试助手的简单实践,让初学者少走弯路,快速体验完整开发流程,再去深入了解或研究其他高深内容。


提示:之所以写这篇文章,也是因为看了许多其他作者的文章大受益处,分享带来共赢

一、成果先行

在这里插入图片描述
此串口调试助手功能还是比较完善的,使用起来丝毫不卡顿,启动速度也相当优秀,可打包成绿色版和安装版,用来代替市面上的串口调试助手是个不错的选择。可最重要的是有了开发技术,随意添加任何其他功能或对界面进行优化。给它取名为QComTool

二、下载安装

1.安装QT

从官网下载安装包:https://download.qt.io/archive/qt/5.14/5.14.2/ 本文使用5.14.2版本,其他版本或许略有不同,尽量别下最新版,用最新版会走更多弯路。下载后直接运行安装。刚进入安装程序会要求输入注册账户,如果闲麻烦可以先断网,再运行安装程序,会自动跳过这一步,再去联网即可。
在这里插入图片描述
组件选择需特别注意,本文只需选择MinGW 7.30 64位系统,用来编译运行,其他组件可以根据需要勾选,由于篇幅限制不展开描述。
在这里插入图片描述
之后根据提示一路下一步即可。
在这里插入图片描述
安装成功后打开开始菜单栏打开Qt Creator软件,是用于应用程序开发的跨平台IDE。
在这里插入图片描述

三、项目开发实战

1.创建新项目

在这里插入图片描述
选择Qt Widgets应用程序
在这里插入图片描述
设置项目路径和名称
在这里插入图片描述
编译系统默认
在这里插入图片描述
基类选择QWidget
在这里插入图片描述
然后一路下一步。创建成功后,直接Ctrl + R编译运行,或者点击左下角小锤子即可出现空白窗口。
在这里插入图片描述

2.UI设计

工程项目目录很简洁,*。pro包含一些配置脚本,一个基类头文件和源文件再加主程序文件。最下面的是设计UI的文件。
在这里插入图片描述
打开UI设计文件,可以很方便的通过拖拽方式设计界面,并且配置属性和名称。
在这里插入图片描述
先添加三个主体框架,修改属性让边框更清晰,再点击阵列分布自动适应窗口大小并对齐。
在这里插入图片描述
其他部分同理,根据需要设计自己的界面,特别注意修改各个控件的名称方便写程序,可在右侧层次结构框架中直接单击修改。设计出以下界面。
在这里插入图片描述
若对这一步有困难,可以直接用记事本打开这个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>757</width>
    <height>529</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <layout class="QGridLayout" name="gridLayout">
   <item row="0" column="0">
    <widget class="QFrame" name="frame">
     <property name="frameShape">
      <enum>QFrame::Box</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <property name="lineWidth">
      <number>2</number>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout">
      <item>
       <layout class="QFormLayout" name="formLayout_2">
        <property name="verticalSpacing">
         <number>20</number>
        </property>
        <item row="0" column="0">
         <widget class="QLabel" name="label">
          <property name="text">
           <string>串口号:</string>
          </property>
         </widget>
        </item>
        <item row="0" column="1">
         <widget class="QComboBox" name="comboBox_1"/>
        </item>
        <item row="1" column="0">
         <widget class="QLabel" name="label_2">
          <property name="text">
           <string>波特率:</string>
          </property>
         </widget>
        </item>
        <item row="1" column="1">
         <widget class="QComboBox" name="comboBox_2"/>
        </item>
        <item row="2" column="0">
         <widget class="QLabel" name="label_3">
          <property name="text">
           <string>校验位:</string>
          </property>
         </widget>
        </item>
        <item row="2" column="1">
         <widget class="QComboBox" name="comboBox_3"/>
        </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="comboBox_4"/>
        </item>
        <item row="4" column="0">
         <widget class="QLabel" name="label_5">
          <property name="text">
           <string>停止位:</string>
          </property>
         </widget>
        </item>
        <item row="4" column="1">
         <widget class="QComboBox" name="comboBox_5"/>
        </item>
        <item row="5" column="0">
         <widget class="QLabel" name="label_6">
          <property name="text">
           <string>流控制:</string>
          </property>
         </widget>
        </item>
        <item row="5" column="1">
         <widget class="QComboBox" name="comboBox_6"/>
        </item>
        <item row="6" column="0">
         <widget class="QLabel" name="label_7">
          <property name="text">
           <string>字编码:</string>
          </property>
         </widget>
        </item>
        <item row="6" column="1">
         <widget class="QComboBox" name="comboBox_7"/>
        </item>
       </layout>
      </item>
      <item>
       <widget class="QPushButton" name="Btn_Serial_Open">
        <property name="text">
         <string>打开串口</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item row="0" column="1">
    <widget class="QFrame" name="frame_2">
     <property name="frameShape">
      <enum>QFrame::Box</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <property name="lineWidth">
      <number>2</number>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout_2">
      <item>
       <widget class="QTextEdit" name="RX_Text">
        <property name="tabChangesFocus">
         <bool>false</bool>
        </property>
        <property name="readOnly">
         <bool>true</bool>
        </property>
       </widget>
      </item>
      <item>
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
         <widget class="QPushButton" name="Btn_Clear">
          <property name="text">
           <string>清空所有</string>
          </property>
          <property name="checkable">
           <bool>false</bool>
          </property>
          <property name="autoDefault">
           <bool>false</bool>
          </property>
          <property name="default">
           <bool>false</bool>
          </property>
          <property name="flat">
           <bool>false</bool>
          </property>
         </widget>
        </item>
        <item>
         <layout class="QHBoxLayout" name="RX_Box">
          <item>
           <widget class="QCheckBox" name="RX_CheckBox_Hex">
            <property name="text">
             <string>Hex</string>
            </property>
            <property name="checked">
             <bool>false</bool>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QCheckBox" name="RX_CheckBox_Format">
            <property name="text">
             <string>日志格式</string>
            </property>
            <property name="checked">
             <bool>true</bool>
            </property>
           </widget>
          </item>
         </layout>
        </item>
       </layout>
      </item>
     </layout>
    </widget>
   </item>
   <item row="1" column="0" colspan="2">
    <widget class="QFrame" name="frame_Send">
     <property name="frameShape">
      <enum>QFrame::Box</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <property name="lineWidth">
      <number>2</number>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout_3">
      <item>
       <widget class="QTextEdit" name="TX_Text"/>
      </item>
      <item>
       <layout class="QHBoxLayout" name="horizontalLayout_4">
        <item>
         <widget class="QPushButton" name="Btn_Send">
          <property name="text">
           <string>发送</string>
          </property>
          <property name="checkable">
           <bool>false</bool>
          </property>
          <property name="checked">
           <bool>false</bool>
          </property>
          <property name="autoDefault">
           <bool>false</bool>
          </property>
          <property name="default">
           <bool>false</bool>
          </property>
          <property name="flat">
           <bool>false</bool>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QFrame" name="frame_4">
          <property name="frameShape">
           <enum>QFrame::StyledPanel</enum>
          </property>
          <property name="frameShadow">
           <enum>QFrame::Raised</enum>
          </property>
          <layout class="QHBoxLayout" name="horizontalLayout_2">
           <item>
            <widget class="QCheckBox" name="Auto_Send">
             <property name="text">
              <string>自动发送</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QSpinBox" name="Wait_ms">
             <property name="minimum">
              <number>1</number>
             </property>
             <property name="maximum">
              <number>10000</number>
             </property>
             <property name="value">
              <number>100</number>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QLabel" name="label_9">
             <property name="text">
              <string>ms</string>
             </property>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item>
         <layout class="QHBoxLayout" name="TX_Box">
          <item>
           <widget class="QCheckBox" name="TX_CheckBox_Hex">
            <property name="text">
             <string>Hex</string>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QLabel" name="TX_Num">
            <property name="text">
             <string>已发送:0</string>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QLabel" name="RX_Num">
            <property name="text">
             <string>已接收:0</string>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QPushButton" name="Btn_Clear_Num">
            <property name="text">
             <string>清零</string>
            </property>
            <property name="checkable">
             <bool>false</bool>
            </property>
            <property name="autoDefault">
             <bool>false</bool>
            </property>
            <property name="default">
             <bool>false</bool>
            </property>
            <property name="flat">
             <bool>false</bool>
            </property>
           </widget>
          </item>
         </layout>
        </item>
       </layout>
      </item>
     </layout>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

3.编写widget.h头文件

widget.h文件中加入以下方法和属性,其中,方法作为自定义槽函数,信号和槽是Qt中的核心概念,通俗的讲就是一个控件状态的改变(如按钮被点击)会发出特定信号,通过connect函数将信号连接到指定的槽函数中,有信号就能触发槽函数运行。

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QSerialPort>
#include <QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    QSerialPort Serial;
    QTimer time;
    quint64 RX_Cnt;
    quint64 TX_Cnt;
    void Set_TX_Hex(bool checked);
    void Check_TX_Text();
    void Clear_Num();
    void ComboBox_AddItems();
    void Serial_Receive();
    void Serial_Ready_Send();
    void Serial_Send();
    void Serial_Tab();
    bool Serial_Open();
    bool Serial_Close();
    void OnTimeOut();


private:
    Ui::Widget *ui;

};
#endif // WIDGET_H

这里报错,不存在串口模块
在这里插入图片描述
需要在pro文件中添加这个模块
在这里插入图片描述

4.编写widget.cpp源文件

这里添加函数实现以及配置各个信号与槽相连接

#include "widget.h"
#include "ui_widget.h"
#include <QSerialPortInfo>
#include <QMetaEnum>
#include <QTextCodec>
#include <QDateTime>
#include <QMessageBox>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("QComTool");
    ui->frame_Send->setMaximumHeight(300);

    RX_Cnt = TX_Cnt = 0;//计数清零
    ui->Btn_Send->setEnabled(false);

    //添加项目
    ComboBox_AddItems();
    //连接信号槽 当下位机发送数据QSerialPor会发送readyRead信号,定义槽Serial_Receive解析数据
    connect(&Serial,&QSerialPort::readyRead,this,&Widget::Serial_Receive);
    //打开串口按钮
    connect(ui->Btn_Serial_Open,&QPushButton::clicked,this,&Widget::Serial_Tab);
    //发送数据按钮
    connect(ui->Btn_Send,&QPushButton::clicked,this,&Widget::Serial_Ready_Send);
    //清空接收按钮
    connect(ui->Btn_Clear,&QPushButton::clicked, ui->RX_Text,&QTextEdit::clear);
    //数据清零按钮
    connect(ui->Btn_Clear_Num,&QPushButton::clicked,this,&Widget::Clear_Num);
    //切换发送为Hex
    connect(ui->TX_CheckBox_Hex,  &QCheckBox::toggled, this,&Widget::Set_TX_Hex);
    //发送框文本改变
    connect(ui->TX_Text,&QTextEdit::textChanged,this,&Widget::Check_TX_Text);
    //自动发送
    connect(&time,&QTimer::timeout,this,&Widget::Serial_Send);
    //自动发送的循环时间
    connect(ui->Wait_ms, QOverload<int>::of(&QSpinBox::valueChanged),
          [=](int i){time.setInterval(i);});//匿名函数
}

Widget::~Widget()
{
    delete ui;
}
//切换发送为Hex
void Widget::Set_TX_Hex(bool checked)
{
    if (checked)
    {
        //编码格式
        QByteArray code_name;
        code_name.append(ui->comboBox_7->currentText());
        QTextCodec *codec = QTextCodec::codecForName(code_name);
        //数据转换
        QByteArray Encoded_data = codec->fromUnicode(ui->TX_Text->toPlainText());
        QString ret(Encoded_data.toHex().toUpper());
        if(ret.length()%2 == 1)
        {
            ret.insert(ret.length()-1,"0");
        }
        int len = ret.length()/2;
        for(int i=0;i<len;i++)
        {
            ret.insert(2*i+i+2," ");
        }
        ui->TX_Text->setText(ret);
    }
    else
    {
        QString ret = ui->TX_Text->toPlainText();
        ret = ret.toUpper();//全部大写
        ret.remove(QRegExp("[^A-F0-9]"));//正则匹配删除非法字符
        QByteArray Data_Hex;
        Data_Hex.append(ret);
        QByteArray Data_Ascii = QByteArray::fromHex(Data_Hex);
        //编码格式
        QByteArray code_name;
        code_name.append(ui->comboBox_7->currentText());
        QTextCodec *codec = QTextCodec::codecForName(code_name);
        //数据转换
        QString Unicode_data = codec->toUnicode(Data_Ascii);
        ui->TX_Text->setText(Unicode_data);
    }
}
//校验发送框字符是否非法
void Widget::Check_TX_Text()
{
    if(ui->TX_CheckBox_Hex->isChecked())
    {
        QString ret1 = ui->TX_Text->toPlainText(),ret2;
        ret2 = ret1.toUpper();
        ret1.remove(QRegExp("[A-F0-9][A-F0-9]\\s"));//先去除合法Hex字符
        if(ret1.length() > 1)
        {
            ret2.remove(QRegExp("[^A-F0-9]"));//删除非法字符
            if(ret2.length()%2 == 1)
            {
                ret2.insert(ret2.length()-1,"0");
            }
            int len = ret2.length()/2;
            for(int i=0;i<len;i++)
            {
                ret2.insert(2*i+i+2," ");
            }
            ui->TX_Text->setText(ret2);
            ui->TX_Text->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
        }
    }
}
//下拉列表框添加项目
void Widget::ComboBox_AddItems()
{
    QMetaEnum metaEnum;
    QStringList strList;
    //comboBox_1 可用串口
    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        ui->comboBox_1->addItem(info.portName());
    }
    //comboBox_2 波特率
    metaEnum = QMetaEnum::fromType<QSerialPort::BaudRate>();
    for (int i = 0; i < metaEnum.keyCount()-1; i++)
    {
        ui->comboBox_2->addItem(QString::number(metaEnum.value(i)));
    }
    ui->comboBox_2->setCurrentText(QString::number(QSerialPort::Baud115200));
    //comboBox_3 校验位
    strList<<"无"<<"奇校验"<<"偶校验"<<"为0"<<"为1";
    ui->comboBox_3->addItems(strList);
    strList.clear();
    //comboBox_4 数据位
    metaEnum = QMetaEnum::fromType<QSerialPort::DataBits>();
    for (int i = 0; i < metaEnum.keyCount()-1; i++)
    {
        ui->comboBox_4->addItem(QString::number(metaEnum.value(i)));
    }
    ui->comboBox_4->setCurrentText(QString::number(QSerialPort::Data8));
    //comboBox_5 停止位
    strList<<"1"<<"1.5"<<"2";
    ui->comboBox_5->addItems(strList);
    strList.clear();
    //comboBox_6 流控
    strList<<"无"<<"硬件"<<"软件";
    ui->comboBox_6->addItems(strList);
    strList.clear();
    //字符编码格式
    strList<<"GBK"<<"UTF-8";
    ui->comboBox_7->addItems(strList);
    strList.clear();
}
//数据清零
void Widget::Clear_Num()
{
    RX_Cnt = TX_Cnt = 0;
    ui->RX_Num->setText("已接收:"+QString::number(RX_Cnt));
    ui->TX_Num->setText("已发送:"+QString::number(TX_Cnt));
}
//接收数据
void Widget::Serial_Receive()
{
    QByteArray data = Serial.readAll();
    RX_Cnt += data.length();
    ui->RX_Num->setText("已接收:"+QString::number(RX_Cnt));
    if(ui->RX_CheckBox_Format->isChecked())//添加日志
    {
        QDateTime time = QDateTime::currentDateTime();
        QString current_date = time.toString("\n【yyyy.MM.dd hh:mm:ss.zzz 接收】\n");
        ui->RX_Text->insertPlainText(current_date);
    }
    if(ui->RX_CheckBox_Hex->isChecked())//Hex模式
    {
        uchar temp;
        QString Hex;
        for (int i = 0; i < data.size(); i++)
        {
            temp = data.at(i);
            if(temp < 0x10)Hex += "0";
            Hex += QString::number(temp,16).toUpper()+" ";
        }
        ui->RX_Text->insertPlainText(Hex);
    }
    else
    {
        //编码格式
        QByteArray code_name;
        code_name.append(ui->comboBox_7->currentText());
        QTextCodec *codec = QTextCodec::codecForName(code_name);
        QString Unicode_data = codec->toUnicode(data);
        ui->RX_Text->insertPlainText(Unicode_data);
    }
    ui->RX_Text->moveCursor(QTextCursor::End);
}
//发送数据准备工作
void Widget::Serial_Ready_Send()
{
    if(ui->Auto_Send->isChecked())//自动发送使能
    {
        if(ui->Btn_Send->text().operator==("发送"))//开始发送
        {
            ui->Btn_Send->setText("停止发送");
            time.setInterval(ui->Wait_ms->value());//循环时间
            time.start();
        }
        else
        {
            ui->Btn_Send->setText("发送");
            time.stop();
        }
    }
    else
    {
        Serial_Send();
    }
}
//发送数据
void Widget::Serial_Send()
{
    if(ui->Auto_Send->isChecked() == false &&
       ui->Btn_Send->text().operator==("停止发送"))//自动发送使能
    {
        ui->Btn_Send->setText("发送");
        time.stop();
        return;
    }
    QByteArray Data_QByteArray;
    if(ui->TX_CheckBox_Hex->isChecked())//Hex格式发送
    {
        QString ret = ui->TX_Text->toPlainText();
        ret = ret.toUpper();//全部大写
        ret.remove(QRegExp("[^A-F0-9]"));//正则匹配删除非法字符
        QByteArray Hex_Srting;
        Hex_Srting.append(ret);
        Data_QByteArray = QByteArray::fromHex(Hex_Srting);
    }
    else//字符串格式发送
    {
        //编码格式
        QByteArray code_name;
        code_name.append(ui->comboBox_7->currentText());
        QTextCodec *codec = QTextCodec::codecForName(code_name);
        //数据发送
        Data_QByteArray = codec->fromUnicode(ui->TX_Text->toPlainText());
    }
    if(Data_QByteArray.length() > 0)
    {
        if(Serial.write(Data_QByteArray) >= 0)
        {
            if(ui->RX_CheckBox_Format->isChecked())//如果开启日志模式
            {
                QDateTime time = QDateTime::currentDateTime();
                QString current_date = time.toString("\n【yyyy.MM.dd hh:mm:ss.zzz 发送】\n");
                current_date += ui->TX_Text->toPlainText();
                ui->RX_Text->insertPlainText(current_date);
                ui->RX_Text->moveCursor(QTextCursor::End);
            }
            TX_Cnt += Data_QByteArray.length();
            ui->TX_Num->setText("已发送:"+QString::number(TX_Cnt));
        }
        else
        {
            QMessageBox::critical(this, tr("错误"), tr("串口发送失败"));
        }

    }
}
//切换串口状态
void Widget::Serial_Tab()
{
    if(ui->Btn_Serial_Open->text().operator==("打开串口"))
    {
        if(Serial_Open())
        {
            ui->Btn_Serial_Open->setText("关闭串口");
            ui->Btn_Send->setEnabled(true);
            ui->Btn_Send->setText("发送");
            time.stop();
        }
        else
        {
            QMessageBox::critical(this, tr("错误"), tr("串口打开失败"));
        }
    }
    else
    {
        if(Serial_Close())
        {
            ui->Btn_Serial_Open->setText("打开串口");
            ui->Btn_Send->setEnabled(false);
        }
    }

}
//打开串口
bool Widget::Serial_Open()
{
    Serial.setParent(this);
    QString PortName = ui->comboBox_1->currentText();
    QString Temp_String;
    //这样我们就获取到 可用的串口名字了
    Serial.setPortName(PortName);
    if(Serial.isOpen())//如果串口已经打开了 先给他关闭了
    {
        Serial.clear();
        Serial.close();
    }
    if(Serial.open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
    {
        //波特率设置
        Serial.setBaudRate(ui->comboBox_2->currentText().toInt(),QSerialPort::AllDirections);//设置波特率和读写方向
        //校验位设置
        Temp_String = ui->comboBox_3->currentText();
        QSerialPort::Parity Serial_Parity;
        if(Temp_String.operator == ("无" ))
            Serial_Parity = QSerialPort::NoParity;
        else if (Temp_String.operator == ("奇校验"))
            Serial_Parity = QSerialPort::EvenParity;
        else if (Temp_String.operator == ("偶校验"))
            Serial_Parity = QSerialPort::OddParity;
        else if (Temp_String.operator == ("为0"))
            Serial_Parity = QSerialPort::SpaceParity;
        else if (Temp_String.operator == ("为1"))
            Serial_Parity = QSerialPort::MarkParity;
        else
            Serial_Parity = QSerialPort::UnknownParity;
        Serial.setParity(Serial_Parity);
        //数据位设置
        Temp_String = ui->comboBox_4->currentText();
        QSerialPort::DataBits Serial_DataBits;
        if(Temp_String.operator == ("5"))
            Serial_DataBits = QSerialPort::Data5;
        else if (Temp_String.operator == ("6"))
            Serial_DataBits = QSerialPort::Data6;
        else if (Temp_String.operator == ("7"))
            Serial_DataBits = QSerialPort::Data7;
        else if (Temp_String.operator == ("8"))
            Serial_DataBits = QSerialPort::Data8;
        else
            Serial_DataBits = QSerialPort::UnknownDataBits;
        Serial.setDataBits(Serial_DataBits);		//数据位
        //停止位
        Temp_String = ui->comboBox_5->currentText();
        QSerialPort::StopBits Serial_StopBits;
        if(Temp_String.operator == ("1"))
            Serial_StopBits = QSerialPort::OneStop;
        else if (Temp_String.operator == ("1.5"))
            Serial_StopBits = QSerialPort::OneAndHalfStop;
        else if (Temp_String.operator == ("2"))
            Serial_StopBits = QSerialPort::TwoStop;
        else
            Serial_StopBits = QSerialPort::UnknownStopBits;
        Serial.setStopBits(Serial_StopBits);
        //流控制
        Temp_String = ui->comboBox_6->currentText();
        QSerialPort::FlowControl Serial_FlowControl;
        if(Temp_String.operator == ("无"))
            Serial_FlowControl = QSerialPort::NoFlowControl;
        else if (Temp_String.operator == ("硬件"))
            Serial_FlowControl = QSerialPort::HardwareControl;
        else if (Temp_String.operator == ("软件"))
            Serial_FlowControl = QSerialPort::SoftwareControl;
        else
            Serial_FlowControl = QSerialPort::UnknownFlowControl;
        Serial.setFlowControl(Serial_FlowControl);
        return true;
    }
    return false;
}
//关闭串口
bool Widget::Serial_Close()
{
    if(Serial.isOpen())//关闭旧串口
    {
        Serial.clear();
        Serial.close();
        return true;
    }
    return false;
}

四、编译运行

到这里可能还有一些列报错,不过没关系,只要按照以上步骤,再点击编译后就不会报错。先给它添加一个图标,可将以下图片通过这个网站https://www.aconvert.com/cn/icon/jpg-to-ico/生成ICO图标,命名为QComTool.ico放到项目文件夹中,
请添加图片描述
再Pro文件中加入这一行代码添加图标资源RC_ICONS = QComTool.ico

在这里插入图片描述
Ctrl+R编译运行:
在这里插入图片描述

五、项目打包

1.编译发布版

选择发布版进行编译,同样Ctrl+R
在这里插入图片描述
于是正常了发布版程序
在这里插入图片描述
在这里插入图片描述
但此时这个程序还不能运行,需要链接动态库文件,也就是传说中的dll.

2.绿色版打包

新建一个目录,命名为QComTool
将exe文件放到这个目录中
在这里插入图片描述

在开始菜单栏中找到 MinGW
在这里插入图片描述
运行代码

windeployqt.exe 文件绝对路径\QComTool.exe

程序会自动将所有动态链接库复制到这个目录中
在这里插入图片描述
这样就得到了一个绿色版的程序,大功告成,点击exe即可运行。

3.安装版打包

如果还需要打包成安装版,可以使用工具 innosetup-6.2.1.exe 进行打包,官网下载链接:
https://jrsoftware.org/isinfo.php,只需通过这个工具把绿色版整个文件夹打包成安装包程序即可。

总结

通过本文实现了串口调试助手功能,但篇幅限制没有展开描述,相信有C++基础的基本能够看懂程序,更多功能等待探索。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

极速入门体验Qt5软件开发,从安装到打包,少走弯路,Qt入门指南,串口调试助手开发实战 的相关文章

随机推荐

  • Java 方法

    方法概述 方法 Method 是将具有独立功能的代码块组织成为一个整体 使其具有特殊功能的代码集 方法必须先创建才可以使用 该过程称为方法定义 方法创建后并不是直接运行的 需要手动使用后才执行 该过程称为方法调用 方法的定义和调用 格式 1
  • RAIN{INDIE} 自动寻路

    Unity游戏中有较多的自动寻路插件 看过几个 大同小异 而RAIN中的Behavior Tree感觉很好 听名字就知道很条理 下面 就用它做个简单的寻路小例子 首先 导入RAIN的包 结构如下 在使用的过程当中还会产生几个文件夹用来存放E
  • Go: 协程的生命周期管理

    协程的生命周期 1 定义 协程的创建等全部生命历程的管理 通俗的讲就是 保姆 它的作用是便于协程的回收利用 goroutine申请的代价很小 但是在go程序中 goroutine的总量是有上限 超过上限 多余出来的协程就得等到前面的协程完成
  • jszip 解压压缩包_一文彻底弄懂jszip中的压缩与解压

    最近在做一个类似离线包的需求 平时我们正常工作中是开发完成一个完整的SPA之后打包上线 由于我们暂时没有服务器的资源 所以目前采取的一个方案是把这个SPA打包为一个zip包 然后上传至CDN 别人用的时候直接从CDN拉包然后解压就可以了 这
  • equals,contentEquals

    直接看源码 还是挺好看的
  • docker配置nginx并绑定域名和映射网站目录

    1 拉取nginx镜像 docker pull nginx 2 映射docker中nginx容器html目录到服务器html配置目录 docker run name my nginx d p 80 80 v data nginx html
  • pip镜像源大全及配置

    在中国使用pip时 可以配置国内镜像源来提高安装速度和稳定性 以下是一些常见的国内镜像源 阿里云 https mirrors aliyun com pypi simple 清华大学 https pypi tuna tsinghua edu
  • Cpp学习——类与对象3

    目录 一 初始化列表 1 初始化列表的使用 2 初始化列表的特点 3 必须要使用初始化列表的场景 二 单参数构造函数的隐式类型转换 1 内置类型的隐式类型转换 2 自定义类型的隐式类型转换 3 多参数构造函数的隐式类型转换 4 当你不想要发
  • 0343基于STM32单片机的茶园大棚环境土壤湿度光强WiFi监测系统proteus仿真原理图PCB

    功能介绍 0 本系统采用STM32F103RCT6作为单片机 1 采用的LCD1602液晶实时显示当前光强土壤湿度等参数信息 2 当土壤湿度超过设定的阈值 蜂鸣器报警 同时控制水泵工作状态 3 按键可更改环境参数的阈值 4 通过WiFi模块
  • 简单对比一下Cookie和Session的主要区别

    一句话总结 Cookie是检查用户身上的 通行证 来确认用户的身份 Session就是通过检查服务器上的 客户明细表 来确认用户的身份的 Session相当于在服务器中建立了一份 客户明细表 注释 300 20 4kb
  • 记录使用fiddle对夜神模拟器抓包相关设置

    在网上百度很多 试了不少 最后试到这个时 成功实现对模拟器的抓包操作 以此记录 注意 设置完后 不开启 Fiddle 的话 模拟器就不能上网了 可以通过再把网络配置 改回去 就可以恢复网络正常访问了 一 配置Fiddle参数设置 1 Too
  • find命令详解

    前言 find命令是我们日常工作中比较常用的Linux命令 全面的掌握这个命令可以使很多操作达到事半功倍的效果 如果对find命令有以下这些疑惑 本文都能帮你解决 find命令的格式是什么 参数中出现 或 号是什么意思 比如find mti
  • JAVA-WEB项目中,前后台结合AES和RSA对数据加密处理

    实际项目中为了系统安全 我们经常需要对请求数据和响应数据做加密处理 这里以spring后台 vue前台的java web为例 记录一个实现过程 一 为什么要结合AES和RSA 因为AES是对称加密 即加密解密用的秘钥是一样 这样一来AES的
  • SQLyog连接mysql

    mysql 64位客户端下载地址 https pan baidu com s 1bYd1YQ 启动mysql安装 cmd 用户名mysql80 密码 123456 安装SQLyog 官网地址 或者装客户端 名称随意 注册码 ccbfc13e
  • C# ASP.NET 如何判断DropdownList是否为空

    程序运行时 鼠标下拉 DropdownList控件 发现下拉列表为空 参见下图 如何判断 DropdownList控件为空呢 参见下述代码 if DropDownList1 Items Count 0 Response Write retu
  • 【win】引用的账户当前已经锁定,且可能无法登陆

    错误信息 错误原因 原保留的登录账号已失效 解决方法 1 打开控制面板 gt 选择 用户账户 2 选择 管理你的凭据 3 选择 windows凭据 展开10 10 0 2登入项 选择 删除 或 编辑 即编辑为现在可用的账户和密码 5 运行
  • Android Studio 中Gradle Build时报错:请求的操作无法在使用用户映射区域打开的文件上

    今天在运行Android项目 Android Studio 中Gradle Build时报错 请求的操作无法在使用用户映射区域打开的文件上执行 1 问题描述 Error java io FileNotFoundException F And
  • 大模型学习 -- CLIP

    本文是CLIP算法的学习笔记 从CLIP算法介绍到具体实现原理 再到应用方法和后续一些优化策略来学习CLIP系列算法 CLIP是什么 CLIP全称是Contrastive Language Image Pre training 一种基于对比
  • 堆叠查询注入攻击

    堆叠注入原理及介绍 Stacked injections 堆叠注入 从名词的含义就可以看到应该是一堆sql语句 多条 一起执行 而在真实的运用中也是这样的 我们知道在mysql中 主要是命令行中 每一条语句结尾加 表示语句结束 这样我们就想
  • 极速入门体验Qt5软件开发,从安装到打包,少走弯路,Qt入门指南,串口调试助手开发实战

    文章目录 前言 一 成果先行 二 下载安装 1 安装QT 三 项目开发实战 1 创建新项目 2 UI设计 3 编写widget h头文件 4 编写widget cpp源文件 四 编译运行 五 项目打包 1 编译发布版 2 绿色版打包 3 安