解析串口数据并通过GUI显示应用开发-Python
? 本片文章主要介绍如何通过Python开发一款UI界面的应用,该应用能实现对来自串口数据的接收和解析,将解析后的数据显示在UI界面上,同时实现数据帧接收频率的计算与超时接收的监测。环境:PyCharm Community Edition 2021.2、Python 3.7.6(高版本在安装pyqt5-tools是会出现报错)
一、GUI绘制
1.1、安装UI设计的第三方库
首先安装PyQT5,可以在window的命令提示符从清华源进行安装
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple PyQT5
再安装pyqt5-tools包,这个第三方库包含qtdesinger工具
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyqt5-tools
1.2、在Pycharm中配置外部工具环境
- 先点击Pycharm右上角的齿轮按键,进入设置页面
添加qtdesinger工具(绘制ui文件),变量配置如下
D:\APP\Python3.7.6\Lib\site-packages\qt5_applications\Qt\bin\designer.exe (这个找到自己designer.exe所在的目录)
$FileDir$\$FileName$
$FileDir$
同样添加pyuic5工具(将制作的ui文件,转化成Py文件),变量配置如下
D:\APP\Python3.7.6\Scripts\pyuic5.exe (这个找到自己pyuic5.exe所在的目录)
$FileName$ -o $FileNameWithoutExtension$.py
$FileDir$
最后添加pyrcc工具,变量配置如下
D:\APP\Python3.7.6\Scripts\pyuic5.exe (这个找到自己pyuic5.exe所在的目录)
$FileName$ -o $FileNameWithoutExtension$.py ,working directory:$FileDir$
D:\APP\Python3.7.6\Scripts (这个找到自己py安装的目录)
1.3、利用qtdesinger绘制UI界面
首先从 工具->External Tools->qtdesinger,启动qtdesinger
可以创建一个Main Window窗口,在窗口内部绘制UI界面
这里就不具体地用文字描述整个ui界面的搭建过程,就简单介绍一下整个界面和需要使用的组件,以及需要修改的属性。QT Desinger的界面如下,上方是一些调整组件布局的工具;左侧是ui界面上的一些组件(按键、标签、文本框之类的);可以将左侧的组件拖到中间的界面,然后通过上方的布局工具,调整这些工具的位置;当选中一个组件后,可以通过右侧的属性框,修改组件的一些属性(这里建议把所有组件的Name都改一下,方便后面程序设计好找到对应的组件)
将设计好的ui文件保存到工程所在的目录下面
1.4、测试UI界面
右击ui文件 -> External Tools -> pyuic5,将刚刚保存的ui文件转成py文件
在生成的py文件后面添加如下程序,
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_MainInfo()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
右击运行该文件的结果
二、串口数据接收与解析
2.1、安装串口第三方库
Python使用串口需要安装pyserial库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyserial
2.2、串口的使用
测试用例
import serial
from time import sleep
ser = serial.Serial()
def port_open_recv():
ser.port='com3'
ser.baudrate=9600
ser.bytesize=8
ser.stopbits=1
ser.parity="N"
ser.open()
if(ser.isOpen()):
print("串口打开成功!")
else:
print("串口打开失败!")
def port_close():
ser.close()
if(ser.isOpen()):
print("串口关闭失败!")
else:
print("串口关闭成功!")
def send(send_data):
if(ser.isOpen()):
ser.write(send_data.encode('utf-8'))
print("发送成功",send_data)
else:
print("发送失败!")
if __name__ == '__main__':
port_open_recv()
while True:
a=input("输入要发送的数据:")
send(a)
sleep(0.5)
常用接口函数
ser.isOpen():查看端口是否被打开。
ser.open() :打开端口‘。
ser.close():关闭端口。
ser.read():从端口读字节数据。默认1个字节。
ser.read_all():从端口接收全部数据。
ser.write("hello"):向端口写数据。
ser.readline():读一行数据。
ser.readlines():读多行数据。
in_waiting():返回接收缓存中的字节数。
flush():等待所有数据写出。
flushInput():丢弃接收缓存中的所有数据。
flushOutput():终止当前写操作,并丢弃发送缓存中的数据。
2.3、数据帧的解析
数据帧的内容如下,一共有12个数据,我们需要获取以逗号分割,等号后面的数据。所以提出数据的思路就不难看出,先将字符串以逗号分割生成12个列表(每个列表的内容也是字符串),再对每个列表的内容以等于号进行分割,取等号后面的内容,具体实现程序如下。
sen1_d=6553,sen2_d=69,sen3_d=23,sen4_d=6553,sen5_d=49,sen6_d=23,sen7_d=6553,sen8_d=31,sen9_d=142,sen10_d=6553,sen11_d=21,sen12_d=6553
def process(message:str):
res = list()
if message.count("=") == 12:
message_list = message.split(",")
for x in message_list :
res.append(x.split("=")[1])
return res
if __name__ == "__main__":
test = "sen1_d=6553,sen2_d=69,sen3_d=23,sen4_d=6553,sen5_d=49,sen6_d=23,sen7_d=6553,sen8_d=31,sen9_d=142,sen10_d=6553,sen11_d=21,sen12_d=6553"
res_list = process(test)
print(len(res_list))
print(res_list)
三、多任务创建
3.1、启动UI界面
创建main.py文件,所需的第三方库,以及自己创建的ui界面和数据帧处理函数
import sys
import time
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from MainInfo import Ui_MainInfo
import serial#导入串口通信库
import data_process
创建自己的ui界面类,该类需要继承QMainWindow 类和自己设计Ui_MainInfo 类,并在初始化函数定义自己需要使用的变量、通过连接槽关联两个线程(串口接收数据线程、监测是否超时线程)以及按键连接函数的具体实现,具体程序实现如下:
class MyMainForm(QMainWindow, Ui_MainInfo):
def __init__(self, parent=None):
super(MyMainForm, self).__init__(parent)
self.setupUi(self)
self.com = "com3"
self.baud = 115200
self.switch_state = bool(0)
self.count = 0
self.start_time = time.time()
self.end_time = time.time()
self.time = 0
self.receive_message = ""
self.check_time_start = time.time()
self.check_time_end = time.time()
self.limit_time = 6.0
self.check = 0
self.connect_button.clicked.connect(self.connection_button_cliked)
self.set_limit_time_button.clicked.connect(self.set_limit_time)
self.thread = process_task()
self.thread.start()
self.thread.sig.connect(self.update_info)
self.check_thread = check_task()
self.check_thread.start()
self.com_value.setText(f"{self.com}")
self.baud_value.setText(f"{self.baud}")
self.limit_time_value.setText(f"{self.limit_time}")
def update_info(self,info):
self.receive_message = info
temp_list = data_process.process(self.receive_message)
if len(temp_list) == 12:
print(self.receive_message)
self.end_time = time.time()
self.time = self.end_time - self.start_time
if self.count == 0:
self.count = 1
else:
self.fps_value.setText(str(self.time))
self.f1_value.setText(temp_list[5])
self.f2_value.setText(temp_list[4])
self.f3_value.setText(temp_list[3])
self.f4_value.setText(temp_list[2])
self.f5_value.setText(temp_list[1])
self.f6_value.setText(temp_list[0])
self.r1_value.setText(temp_list[11].split('\\')[0])
self.r2_value.setText(temp_list[10])
self.r3_value.setText(temp_list[9])
self.r4_value.setText(temp_list[8])
self.r5_value.setText(temp_list[7])
self.r6_value.setText(temp_list[6])
self.start_time = time.time()
self.check_time_start = time.time()
self.check = 1
def connection_button_cliked(self):
if self.switch_state == 0:
self.connect_button.setText("断开")
self.baud = int(self.baud_value.text())
self.com = str(self.com_value.text())
self.message.append(f"com:{self.com},baud:{self.baud}")
port_open_recv(self.com,self.baud)
self.message.append("Connected")
self.check = 0
else:
self.connect_button.setText("连接")
ser.close()
self.message.append("Disconnect")
self.switch_state = bool(1 - self.switch_state)
def set_limit_time(self):
self.limit_time = float(self.limit_time_value.text())
self.message.append(f"下限时间修改为:{self.limit_time}")
然后在if __name__ == "__main__": 实例化MyMainForm 对象并启动界面
if __name__ == "__main__":
app = QApplication(sys.argv)
myWin = MyMainForm()
myWin.show()
sys.exit(app.exec_())
3.2、创建串口接收任务
在MyMainForm 类的初始化函数中已经连接并启动了process_task 线程,这里就需要具体实现process_task 的内容,首先process_task 类需要继承QThread 类,并重写run 函数,在run 函数中实现自己设计的功能,具体程序实现如下:
class process_task(QThread):
sig = pyqtSignal(str)
def __init__(self, parent=None):
super(process_task, self).__init__(parent)
self.receistr = " "
def run(self):
while True:
time.sleep(0.01)
if ser.isOpen() :
self.receistr = ser.readline()
self.sig.emit(str(self.receistr))
else:
pass
3.3、创建接收超时监测任务
超时监测任务的创建方式和串口接收数据的任务创建方式相同,需要在run 函数里面具体实现自己的功能,这里我是在run函数里面判断串口是否打开,如果打开则开始监测时间,如果实现超过设定的时间,则在ui界面给出相应的提示。
class check_task(QThread):
def __init__(self, parent=None):
super(check_task, self).__init__(parent)
def run(self):
while True:
time.sleep(0.01)
if ser.isOpen() :
myWin.check_time_end = time.time()
if myWin.check_time_end - myWin.check_time_start > myWin.limit_time :
if myWin.check == 1 :
myWin.message.append("接收超时")
myWin.check_time_start = time.time()
else:
pass
四、将py文件转为exe文件
4.1、安装第三方工具库
py文件转为exe文件需要依赖pyinstaller第三方库实现,所以首先通过命令输入以下命令安装第三方库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller
4.2、执行命令生成exe文件
第三方库安装完成后,通过命令行进入py文件所在目录,然后执行一下命令
pyinstaller -F main.py
# 依据自己的py文件名
然后可以在工程文件的dist目录下找到刚才生成的exe文件
然后双击可以正常运行
|