# micro_flow_EXE **Repository Path**: numda/micro_flow_EXE ## Basic Information - **Project Name**: micro_flow_EXE - **Description**: QT(C++)开发的串口上位机,包含配置文件、串口参数读写、报警、数值显示、动态曲线显示、动态表格显示、日志模块、菜单栏等。仅供个人交流学习使用。 - **Primary Language**: C++ - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 19 - **Created**: 2024-10-09 - **Last Updated**: 2024-10-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
# 修订历史 | 日期 | 作者 | 版本 | 备注 | | :-------: | :-------: | :--: | :--------------------: | | 2021-4-26 | Jiaqin Bi | 1.0 | 初稿 | | 2021-4-30 | Jiaqin Bi | 1.1 | 新增函数列表、优化代码 | | 2021-5-8 | Jiaqin Bi | 1.2 | 优化代码、修改函数列表 | # 一、QT Creator使用指南 软件下载地址:链接:https://pan.baidu.com/s/1sEofnZX4B88qOgLY1SwWGQ 提取码:1234 软件安装方式:默认安装。 ## 1、新建项目 1. 选择模板  2. 创建名称,选择路径  3. windows下编译直接选择qmake 4. 填写类名,并选择基类,自行选择是否需要ui界面设计文件  基类中QMainWindow为带有菜单栏的类;QWidget为单个窗口的类;QDialog为对话框的类。 当勾选了Generate form时上位机界面可以通过组件拖拽的形式布局,修改起来比较灵活;若未勾选则需要自行通过代码创建组件并设计布局。 5. 接下来一路默认即可。项目文件以及运行界面如下所示:
项目列表中.pro文件为工程文件,QT Creator打开工程时需要找到此文件;
Headers文件夹下的为头文件;
Sources下的为cpp源文件;
Forms下的为ui设计文件;
## 2、项目运行
QT Creator左下角一般在开发过程中选择Debug方式编译运行,在发布时选择Release版本。点击运行按钮为编译运行,点击Debug按钮为Debug方式运行,点击锤子构建按钮为编译项目。如下所示:

快捷键:Ctrl+B 编译不运行 ; Ctrl+R 编译运行。
## 3、新建类文件
1. 在项目下文件夹上右键选择Add new…
2. 若需要添加源文件和头文件则选择C++ Class,选择下面两个则只创建相应的单个文件。
3. 填入类名并选择基类,点击下一步。如下所示:
4. 直接下一步直到完成即可。最后会创建两个文件,如下所示:
## 4、UI界面设计指南
双击.ui文件会进入布局页面,如下所示:
设计完成之后,点击左边工具栏编辑、可以看到.ui文件的源码(只读),可以看到源码都是标签和属性。如下所示:
## 5、简单示例
设计一个点击按钮控制文字的显示隐藏的简易工程。
1. 双击.ui文件进入设计界面,调整界面大小(光标在界面右下角拖住调整即可)
2. 拖入一个按钮组件(Push Button)到界面中,双击拖入的按钮修改名称为“隐藏”
3. 选中按钮,到属性区找到objectName修改按钮组件名为“btn_showText”
4. 继续拖入一个Label组件,双击可以修改默认显示文字,然后按照3的步骤修改组件名为lb_text
5. 进入主类文件mainwindow.cpp(此处以新建项目widget_DEMO为例)
6. 在mainwindow.h文件的第18行添加如下代码
```c++
public slots:
void btn_showText_click(); //添加槽函数声明
```
7. 在mainwindow.cpp文件的默认构造函数中添加信号与槽的连接代码
```c++
connect(ui->btn_showText, &QPushButton::clicked, this, &MainWindow::btn_showText_click);
```
8. 实例化槽函数,代码如下:
```c++
void MainWindow::btn_showText_click()
{
static bool show_flag = true;
show_flag = !show_flag;
ui->lb_text->setVisible(show_flag);
}
```
9. 效果如下:

点击按钮隐藏文字,再次点击又显示文字。
## 6、信号与槽
关于5中示例里面使用到的**信号与槽**是QT中传递信号时最常用的方法。具体介绍此处不做说明。
## 7、开发语言与开发方式
QT开发比较灵活,有原生的QT(C++)的开发方式,也有pyqt的方式(qt做界面,python写后台)。本次项目中使用的开发语言为C++开发。
# 二、界面设计(690*440)
## 1、首页
## 2、第二页

## 3、第三页

## 4、菜单弹出框(455*195)

# 三、组件使用指南(部分)
## 1、Tab Widget
类似于标签页的形式,两个`tab`页面的控件互不影响,但是可以直接访问。
### 1.1、使用方法

如上图所示,将`Tab Widget`组件拖拽到`ui`页面中,同时调整大小和标题。显示时两个tab页面或者多个tab页面互不影响。
选中`tabWdiget`组件。点击需要修改名称的`tab`页,在属性中找到`currentTabText`即可修改显示名称,如下所示:

### 1.2、访问方法
直接通过`ui`指针访问即可。
# 四、串口通信设计
## 1、简介
串口通信主要使用到的类为`QT`提供的`QSerialPort`,可以实现配置、连接以及数据的收发。
## 2、QSerialPort类使用指南
1. 在.pro工程文件中添加`QT += serialport`,重新编译一次;
2. 添加头文件
````c++
#include
2、然后模板根据自己的需求选择即可,如下所示
3、然后自定义类名,选择下一步,直接点完成即可
## 2、添加页面代码
代码如下:
```c++
//1 添加头文件
#include "dialogabout.h" //关于弹窗
...
...
...
//2 添加指针变量
private:
DialogAbout *dialog_about;
...
...
...
//3 添加槽函数
public slots:
void menu_about_click(); //关于
...
...
...
//4 连接槽函数与信号
connect(ui->m1_about, SIGNAL(triggered()), this, SLOT(menu_about_click()));
//5 完善槽函数
void Serial::menu_about_click()
{
dialog_about = new DialogAbout(this);
dialog_about->setModal(false);
dialog_about->setWindowTitle("关于");
dialog_about->show();
}
```
## 3、效果
效果如下所示:

# 十三、多线程编程
## 1、 相关知识
- 进程支持多线程,但是必须有一个主进程
- 多个线程之间是相互独立的
- 主线程具有特殊性
- 其它线程都是通过主线程 直接或间接启动的
- 主线程结束,其它所有线程都要结束
- 子线程退出不影响主线程
- 多线程不是必须的,大多数情况下,一个主线程就可以完成工作
- 线程越多,对系统造成的负担越大
- 跨线程操作,则使用**信号与槽**
## 2、 实现的两种方法
- `QThread`(较为传统)
- `QtConcurrent`(高级API,较为方便)
# 十四、问题与解决方法
## 1、调用默认浏览器打开网页
```c++
QString URL = "https://wwwbaidu.com/";
QDesktopServices::openUrl(QUrl(URL.toLatin1()));
```
## 2、 上位机图标的修改
1. 在[Iconfont-阿里巴巴矢量图标库](https://www.iconfont.cn/)网站下载相应图标,32x32 48x48均可;
2. 在[PNG转ICO - 在线转换图标文件](https://www.aconvert.com/cn/icon/png-to-ico/)网站转换成ico;
3. 复制ico文件到QT工程源代码目录;
4. 在.pro文件中修改RC_ICONS = 新的ico图标
## 3、标题的修改
```c++
this->setWindowTitle("标题");
```
## 4、一个CPP使用另一个CPP的成员
参考连接:https://blog.csdn.net/qq_38836568/article/details/114502260
以serial.cpp(主文件)和chartfigure.cpp为例,chartfigure.cpp使用serial.cpp中的成员方法
1. 在serial.h类中的public下添加静态指针,如下所示
```c++
static Serial *serialUI; //静态指针变量
```
2. 在serial.cpp全局变量中初始化静态指针,如下所示
```c++
Serial *Serial::serialUI = nullptr; //初始化
```
3. 在serial.cpp类中静态指针给静态指针赋值,如下所示
```c++
serialUI = this; //赋值
```
4. 在chartfigure.cpp使用serial.cpp中的show_tips方法
```c++
//show_tips("图表已使用深色主题");
Serial::serialUI->show_tips("图表已使用深色主题");
```
## 5、一个UI使用另一个UI的控件
方法:指针传参
以serial.cpp(主文件)和charttable.cpp为例,charttable.cpp控制serial.cpp中的控件
1. 在charttable.h中添加头文件,如下所示
```c++
#include "ui_serial.h"
```
2. 在charttable.cpp中public中添加成员方法,如下所示
```c++
void insert_item(float temper, float flow, Ui::Serial *ui); //表格插入数据
```
3. 使用如下所示
```c++
uiS->le_Upper_temper->text()
```
4. 在serial.cpp中调用charttable.cpp的insert_item成员方法时直接传入ui即可。
## 6、配置文件中文乱码
- 首先读取时设置文件的编码格式(`GB2312`),如下所示
```c++
QString iniFilePath = QCoreApplication::applicationDirPath()+"/config.ini"; //配置文件路径
QSettings settings(iniFilePath, QSettings::IniFormat); //设定QSettings配置文件的名称
settings.setIniCodec(QTextCodec::codecForName("GB2312")); //设置配置文件的编码格式,防止中文乱码
```
- 使用`nodepad++`重新修改配置文件的编码格式并保存,如下所示:

# 十五、工程文件使用介绍
## 1、工程目录
使用QT打开工程文件之后,目录结构如下所示:

介绍如下:
```python
micro_flow_EXE/ #工程名
| -- micro_flow_EXE.pro #工程文件
| -- Headers/ #头文件文件夹
| | |-- chartfigure.h #曲线图表页头文件
| | |-- charttable.h #动态表格页头文件
| | |-- dialogabout.h #菜单栏关于弹窗头文件
| | |-- dialogcompany.h #菜单栏公司简介弹窗头文件
| | |-- modbuscrc.h #modbus crc计算头文件
| | |-- serial.h #主页面文件头文件
| | |-- typeconver.h #类型转换头文件
| -- Sources/ #源文件文件夹
| | |-- chartfigure.cpp #曲线图表页源文件
| | |-- charttable.cpp #动态表格页源文件
| | |-- dialogabout.cpp #菜单栏关于弹窗源文件
| | |-- dialogcompany.cpp #菜单栏公司简介弹窗源文件
| | |-- modbuscrc.cpp #modbus crc计算源文件
| | |-- serial.cpp #主页面文件源文件 主页面
| | |-- typeconver.cpp #类型转换源文件
| -- Forms/ #UI表单页
| | |-- chartfigure.ui #曲线图表页ui表单文件
| | |-- charttable.ui #动态表格页ui表单文件
| | |-- dialogabout.ui #菜单栏关于弹窗ui表单文件
| | |-- dialogcompany.ui #菜单栏公司简介弹窗ui表单文件
| | |-- serial.h #主页面文件ui表单文件
| -- Resources/ #资源文件夹
```
## 2、函数列表(部分)
### 1、初始化部分
```c++
////文件--Serial.cpp Serial.h
/**
* @brief 系统初始化函数 按钮、图表等默认状态
*/
void Serial::system_init(){}
/**
* @brief 初始化发送命令
*/
void Serial::cmd_init_ready(){}
/**
* @brief 临时文件(夹)初始化
*/
void Serial::file_fold_init(){}
```
### 2、串口通信
```c++
////文件--Serial.cpp Serial.h
/**
* @brief 按照用户选择或者默认ini配置打开串口
*/
void Serial::btn_OpenSerial_clicked(){}
/**
* @brief 关闭串口
*/
void Serial::btn_CloseSerial_clicked(){}
/**
* @brief 系统定时发送发送数据帧请求接收数据
* 连续10次(10s)接收空数据帧判定从机掉线,进行软件弹框提示
* 同时判断串口是否连接正常
*/
void Serial::auto_send_recv_data(){}
/**
* @brief 插拔串口之后手动扫描串口设备
*/
void Serial::btn_ScanSerial_clicked(){}
/**
* @brief 主动获取计算机所有串口信息,并读取ini文件设置默认显示的串口号
*/
void Serial::getSerialPort(){}
```
### 3、CRC计算
```c++
////文件--modbuscrc.cpp modbuscrc.h
/**
* @brief 计算CRC校验值,返回CRC校验值
* @param 需要计算校验位的前六位十六进制字符串
* @return crc校验位十六进制字符串
*/
QString crcCalculation(QString message){}
```
### 4、修改模块地址等参数
```c++
////文件--Serial.cpp Serial.h
/**
* @brief 设置从机地址,弹框修改
* 功能码:06 写地址
* 数据帧格式:原地址 06 00 02 新地址(1-255,占四位) 校验码高位 校验码低位
*/
void Serial::btn_set_slave_addr(){}
/**
* @brief 设置modbus串口连接参数
* 参数全部取自下拉框
* 1.模块地址:
* 2.功 能 码:06
* 3.寄存器地址:
* 4.数 据:
* ---------------------------------------------------------------
* 15-12位波特率 11-8位--数据位 7-4位--停止位 0-3位--奇偶校验位
* 0--4800 0--8 0--1 0--None
* 1--9600 1--9 1--1.5 1--Odd
* 2--19200 2--无 2--2 2--Even
* 3--38400 3--无 3--无 3--无
* 4--43000 4--无 4--无 4--无
* 5--56000 5--无 5--无 5--无
* 6--57600 6--无 6--无 6--无
* 7--115200 7--无 7--无 7--无
* ---------------------------------------------------------------
* 5.CRC校验位:
* 例子:修改01从机通信参数为波特率9600,8位数据位,1位停止位,无奇偶校验位的数据帧为01 06 00 03 10 00 74 0A(16 进制)
*/
void Serial::btn_set_serial_param(){}
/**
* @brief 还原modbus串口连接参数
* 波特率--9600
* 数据位--8
* 停止位--1
* 奇偶校验位--None
*/
void Serial::btn_back_serial_param(){}
```
### 5、数码管数据显示页
数码管显示在auto_send_recv_data()函数中已完成,下面是数值的填充代码以及数据保存等操作函数
```c++
////文件--Serial.cpp Serial.h
ui->lcd_TemperValue->setDigitCount(5); //数码管数字显示位数
ui->lcd_TemperValue->setStyleSheet("background-color: yellow"); //读取到数字之后的背景色
ui->lcd_TemperValue->display(obj_param.temper_f); //将读取到的温度数值显示到数码管上
ui->lcd_FlowValue->setDigitCount(5);
ui->lcd_FlowValue->setStyleSheet("background-color: yellow");
ui->lcd_FlowValue->display(obj_param.flow_f);
/**
* @brief 保存流量数据到指定文件
*/
void Serial::save_flowDataTo_file(){}
/**
* @brief 保存温度数据到指定文件
*/
void Serial::save_temperDataTo_file(){}
/**
* @brief 调用本机记事本程序打开历史温度数据
*/
void Serial::query_temperData_history(){}
/**
* @brief 调用本机记事本程序打开历史流量数据
*/
void Serial::query_flowData_history(){}
/**
* @brief 分割温度和流量
* @param str
*/
void Serial::ArraySplit(QByteArray str){}
```
### 6、曲线显示页
```c++
////文件--chartfigure.cpp chartfigure.h
/**
* @brief 图表初始化
*/
void ChartFigure::chart_init(){}
/**
* @brief 追加点、平滑曲线、动态更新数据
*/
void ChartFigure::DrawLine(qreal x, qreal y1, qreal y2){}
/**
* @brief 深色主题按钮
*/
void ChartFigure::btn_chart_dark_click(){}
/**
* @brief 浅色主题按钮
*/
void ChartFigure::btn_chart_light_click(){}
/**
* @brief 检查图表曲线显示状态
*/
void ChartFigure::check_chart_state(){}
/**
* @brief 显示温度曲线
*/
void ChartFigure::cb_temper_show(){}
/**
* @brief 显示流量曲线
*/
void ChartFigure::cb_flow_show(){}
/**
* @brief 显示全部曲线
*/
void ChartFigure::cb_show_all(){}
/**
* @brief 隐藏全部曲线
*/
void ChartFigure::cb_hide_all(){}
```
### 7、表格显示页
```c++
////文件--charttable.cpp charttable.h
/**
* @brief 表格初始化
*/
void ChartTable::table_init(){}
/**
* @brief 表格定时插入数据
*/
void ChartTable::insert_item(float temper, float flow, Ui::Serial *uiS){}
/**
* @brief 表格自动翻页复选框
*/
void ChartTable::cb_tscroll_auto(){}
/**
* @brief 表格上一页按钮
*/
void ChartTable::btn_page_up_click(){}
/**
* @brief 表格下一页按钮
*/
void ChartTable::btn_page_down_click(){}
/**
* @brief 表格首页按钮
*/
void ChartTable::btn_page_home_click(){}
/**
* @brief 表格尾页按钮
*/
void ChartTable::btn_page_last_click(){}
/**
* @brief 清空表格
*/
void ChartTable::btn_page_clear_click(){}
```
### 8、自动报警
```c++
////文件--Serial.cpp Serial.h
/**
* @brief 设置温度安全上限
*/
void Serial::set_temper_upper(){}
/**
* @brief 设置温度安全下限
*/
void Serial::set_temper_lower(){}
/**
* @brief 设置流量安全上限
*/
void Serial::set_flow_upper(){}
/**
* @brief 设置流量安全下限 大于0
*/
void Serial::set_flow_lower(){}
/**
* @brief 设置LED报警灯的样式
* @param size
* @param color
*/
void Serial::set_warning_LED(int size, int color){}
/**
* @brief 温度报警复位
*/
void Serial::btn_reset_temper_alarm(){}
/**
* @brief 流量报警复位
*/
void Serial::btn_reset_flow_alarm(){}
/**
* @brief 复位所有报警
*/
void Serial::btn_reset_allParam_alarm(){}
/**
* @brief 还原实时监控数据的安全上下限参数
* 温度上限:50℃
* 温度下限:-10℃
* 流量上限:30 待修改
* 流量下限:0
*/
void Serial::btn_back_safe_limit_param(){}
```
### 9、日志
```c++
////文件--Serial.cpp Serial.h
/**
* @brief 打印操作日志
* @param str, type type==1时为紧急通知,显示红色 type==0时正常显示
*/
void Serial::print_log(QString str, int type){}
/**
* @brief 清除日志
*/
void Serial::btn_clear_log(){}
/**
* @brief 保存当前日志
*/
void Serial::btn_save_log(){}
/**
* @brief QT窗口左下角显示提示信息
* @param tip,传入的提示语
*/
void Serial::show_tips(QString tip){}
```
### 10、菜单栏
```c++
////文件--Serial.cpp Serial.h dialogcompany.h dialogabout.h
/**
* @brief 菜单栏退出
*/
void Serial::menu_exit_click(){}
/**
* @brief 菜单栏打开日志文件
*/
void Serial::menu_open_log_file(){}
/**
* @brief 菜单栏打开日志文件夹
*/
void Serial::menu_open_log_fold(){}
/**
* @brief 打开临时文件夹
*/
void Serial::menu_open_temp_fold(){}
/**
* @brief 菜单栏打开公司官网(调用PC机默认浏览器)
*/
void Serial::menu_company_web(){}
/**
* @brief 菜单栏打开公司简介
*/
void Serial::menu_company_about(){}
/**
* @brief 菜单栏打开关于
*/
void Serial::menu_about_click(){}
```
### 11、更新时间
```c++
////文件--Serial.cpp Serial.h
/**
* @brief 更新系统时间
*/
void Serial::lb_getDateTime(){}
```
### 12、类型转换
```c++
////文件--typeconver.cpp typeconver.h
/**
* @brief 判断字符串是否为整数(仅判断格式,不考虑范围)
* @param char*
* @return
*/
bool isInt(const char* str){}
/**
* @brief 判断字符串是否为浮点数(仅判断格式,不考虑范围)
* @param char *
* @return
*/
bool isFloat(const char * str){}
/**
* @brief 将字符型进制转化为16进制
* @param str
* @return QByteArray
*/
QByteArray QString2Hex(QString str){}
/**
* @brief 将单个字符串转换为hex
* @param ch
* @return char
*/
char ConvertHexChar(char ch){}
```
# 十六、项目打包EXE
1. 项目编译时选择`Release`版本,点击左下角构建编译
2. 点击左边工具栏的项目选项,打开构件目录下的release,复制exe文件到自行指定的英文文件夹下。
3. 打开`QT`命令行工具,我的是`Qt5.9.9(MinGW 5.3.0 32-bit)`,命令行中进入步骤2中的目录
4. 使用`windeployqt`进行链接库的构建,命令为`windeployqt 软件名.exe`,每个人的exe名字不同使用自己的就行,成功之后会看到当前目录下多了很多库文件。

5. 使用`Enigma Virtual Box`进行库文件封装,即将所有文件合并成一个`exe`文件运行,软件界面如下所示:

6. 点击`Enter Input File Name`中选择之前单独复制出来的exe文件,如下所示:

7. 点击左下角`Add->Add Floder Recursive`,选择库文件所在的文件夹(`exe`所在的文件夹),弹框点击`OK`

8. 出现如下界面后执行第9步

9. 点击`Process->RUN`,等待完成`close`即可。如下所示:

10. 成功之后会生成一个带`_boxed.exe`的可执行文件,将其与给出的`config.ini`配置文件拷贝至同一目录即可。