解析串口数据并通过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类名称进行修改
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()defport_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("串口打开失败!")#isOpen()函数来查看串口的开闭状态defport_close():
ser.close()if(ser.isOpen()):print("串口关闭失败!")else:print("串口关闭成功!")defsend(send_data):if(ser.isOpen()):
ser.write(send_data.encode('utf-8'))#编码print("发送成功",send_data)else:print("发送失败!")if __name__ =='__main__':
port_open_recv()whileTrue:
a=input("输入要发送的数据:")
send(a)
sleep(0.5)#起到一个延时的效果,这里如果不加上一个while True,程序执行一次就自动跳出了
常用接口函数
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
defprocess(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
类,并在初始化函数定义自己需要使用的变量、通过连接槽关联两个线程(串口接收数据线程、监测是否超时线程)以及按键连接函数的具体实现,具体程序实现如下:
classMyMainForm(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}")defupdate_info(self,info):
self.receive_message = info
temp_list = data_process.process(self.receive_message)iflen(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 =1else:
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# print(f"context:{self.receive_message},type = {type(self.receive_message)}")defconnection_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 =0else:
self.connect_button.setText("连接")
ser.close()
self.message.append("Disconnect")
self.switch_state =bool(1- self.switch_state)defset_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__":#固定的,PyQt5程序都需要QApplication对象。sys.argv是命令行参数列表,确保程序可以双击运行
app = QApplication(sys.argv)#初始化
myWin = MyMainForm()# 将窗口控件显示在屏幕上
myWin.show()# 程序运行,sys.exit方法确保程序完整退出。
sys.exit(app.exec_())
3.2、创建串口接收任务
在
MyMainForm
类的初始化函数中已经连接并启动了
process_task
线程,这里就需要具体实现
process_task
的内容,首先
process_task
类需要继承
QThread
类,并重写
run
函数,在
run
函数中实现自己设计的功能,具体程序实现如下:
classprocess_task(QThread):
sig = pyqtSignal(str)def__init__(self, parent=None):super(process_task, self).__init__(parent)
self.receistr =" "defrun(self):# 需要执行的内容whileTrue:
time.sleep(0.01)# 提供读取串口是否被打开时间,以免闪退if ser.isOpen():
self.receistr = ser.readline()# 发出信号
self.sig.emit(str(self.receistr))else:pass
3.3、创建接收超时监测任务
超时监测任务的创建方式和串口接收数据的任务创建方式相同,需要在
run
函数里面具体实现自己的功能,这里我是在run函数里面判断串口是否打开,如果打开则开始监测时间,如果实现超过设定的时间,则在ui界面给出相应的提示。
classcheck_task(QThread):def__init__(self, parent=None):super(check_task, self).__init__(parent)defrun(self):# 需要执行的内容whileTrue:
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文件
然后双击可以正常运行
版权归原作者 ViTO_2001 所有, 如有侵权,请联系我们删除。