基于PyQT5解析串口数据并通过GUI显示应用开发-Python

解析串口数据并通过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()

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("串口打开失败!")
#isOpen()函数来查看串口的开闭状态

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)#起到一个延时的效果,这里如果不加上一个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
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
            # print(f"context:{self.receive_message},type = {type(self.receive_message)}")

    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__":
    #固定的,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函数中实现自己设计的功能,具体程序实现如下:

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文件

在这里插入图片描述

然后双击可以正常运行

在这里插入图片描述

  • 8
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值