Python 3.8 + PyCharm + PyQt5(四)-串口助手

  • UI布局
  • 代码编写
  • 模块加载
  • 代码实现
  • 线程
  • 发送数据



所有后面讲解和记录的源码都会在

GuiHub中,可以自行下载


上节我们讲到通过数字输入框,然后做了一个简单的加法,操作了按钮事件,字符转换等。


这次我们来讲讲实现串口助手,通过串口进行通讯。

UI布局

首先创建pycharm工程建立新的UI,并拖拽QT控件,进行UI的布局,布局如下,当然个人喜好,喜欢怎么放就怎么放。

python 操作安卓串口 python做串口助手_Data


当然两个按钮分别对应了2个槽:

一个打开串口槽:

python 操作安卓串口 python做串口助手_Data_02


一个发送给数据槽:

python 操作安卓串口 python做串口助手_python_03


点击保存后,用PyUIC形成.py文件。

代码编写

模块加载

串口小助手,那么我们就需要对串口进行读写,首先我们在python模块中增加串口模块,这边我们说一个新的增加模块的方法,在前面的讲解中,我们有说到增加模块的方法有2中,现在说第三种:
打开pycharm,下面选择这个选项,打开来发现,这个其实是虚拟环境。这边我们首先输入

pip list

会打印出当前加载好的模块,这边显示的pyserial就是我们需要的串口模块。

python 操作安卓串口 python做串口助手_python 操作安卓串口_04


可以在这边输入

pip install pyserial

来对串口模块进行安装。

python 操作安卓串口 python做串口助手_串口通信_05


我这边因为已经安装好,所以提示了当前包的版本信息。

代码实现

串口通信,首先我们得得到电脑的串口号,python的串口接口可以获取到电脑设备的串口列表,

COM_List = list(serial.tools.list_ports.comports())
if len(COM_List) == 0:
   print('无可用串口')
else:
    for i in range(0, len(COM_List)):
        print(COM_List[i])

通过这个接口,我们可以看到最终打印可以看到,我们电脑有2个串口,前面是串口编号,后面是串口的名称。

python 操作安卓串口 python做串口助手_python 操作安卓串口_06


因此,我们需要将这两个放到我们的界面的串口列表中。

这边我们把这部分代码封装成一个函数。

# 获取串口列表
def Get_Com_List():
    return list(serial.tools.list_ports.comports())


# 界面UI按键程序重写
class Mywindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(Mywindow, self).__init__()
        self.setupUi(self)
        COM_List = Get_Com_List()  # 获取串口列表
        for i in range(0, len(COM_List)):  # 将列表导入到下拉框
            self.Com_Port.addItem(COM_List[i].name)

    def Open_Com_Click(self):
        print("点击了打开串口按钮")


    def Send_Data_Click(self):
        print("点击了发送数据按钮")

首先我们重写UI界面,将两个按钮事件进行简单的定义,我们首先完成串口号的获取。

在初始化函数中,我们增加了获取串口号,并且把串口号的名称加入到Com_Port控件中,具体效果是这样的:

python 操作安卓串口 python做串口助手_串口通信_07


串口号中已经可以下拉选择哪个串口。

同理我们把串口波特率也加到下面波特率的下拉框中,效果如图:

python 操作安卓串口 python做串口助手_python 操作安卓串口_08


我这边就只添加了2种进行测试。代码如下:

self.Com_Band.addItem("9600")
        self.Com_Band.addItem("115200")

获取到了串口号了,接下来我们就要来打开串口,发送数据,这边我们需要先知道下拉框选择了哪个串口。
通过接口

self.Com_Port.currentText()

来进行获取当前是COM多少。然后同理选择波特率,然后进行打开串口。:

def Open_Com_Click(self):
        global custom_serial  # 全局变量,需要加global
        global Com_Open_Flag
        print("点击了打开串口按钮")
        if self.Open_Com.text() == "打开串口":
            print(self.Com_Port.currentText())
            custom_serial = serial.Serial(self.Com_Port.currentText(), int(self.Com_Band.currentText()), timeout=0.5)
            if custom_serial.isOpen():
                print("open success")
                Com_Open_Flag = True
                self.Open_Com.setText("关闭串口")
                self.Com_Band.setEnabled(False)  # 串口号和波特率变为不可选择
                self.Com_Port.setEnabled(False)
            else:
                print("open failed")
        else:
            Com_Open_Flag = False
            self.Open_Com.setText("打开串口")
            custom_serial.close()
            self.Com_Band.setEnabled(True)  # 串口号和波特率变为可选择
            self.Com_Port.setEnabled(True)

这是对打开按钮程序的重写,答题意思我想大家以下就看懂了,首先就是判断当前状态,按钮点击是打开串口还是关闭串口,执行不同的操作,还有打开失败还是成功。大家应该很容易看懂。
接下来就是串口的收发数据了。
数据接收
数据接收部分,有一个比较难搞的地方,就是一旦你串口打开了,那么数据就必须要一直处于接收状态,如果你按照往常的使用while True来使用,你会发现你的显示界面卡着不动了。这边我们需要引入新的知识点:

线程

线程简单应用,就是打开线程,然后一直读串口数据:

import _thread

# 获取串口数据
def Com_Data_Rsv(threadName):
    while True:
        while Com_Open_Flag:
            data = custom_serial.read_all()
            if data == b'':
                continue
            else:
                print("receive : ", data)
                window.Set_Display_Data(data)
                
_thread.start_new_thread(Com_Data_Rsv, ("Thread-1",))

然后在UI界面的类中添加方法:

def Set_Display_Data(self, Data):
        self.Date_Display.insertPlainText(str(Data, encoding="utf-8"))  # 显示数据

打开线程,一直接收串口数据,只是串口处于打开情况下,我们才处理接收数据,最后把数据打印在界面上显示。

具体效果如下:

python 操作安卓串口 python做串口助手_串口通信_09

发送数据

综上所述,分为以下几个步骤
1、按钮事件重写
2、获取发送文本
3、通过串口发送
代码如下:

def Send_Data_Click(self):
        print("点击了发送数据按钮")
        Data_Need_Send = self.Send_Data_Dsiplay.toPlainText()
        if custom_serial.isOpen():
            custom_serial.write(Data_Need_Send.encode("gbk"))
        else:
            print("请先打开串口")

最终效果:

python 操作安卓串口 python做串口助手_python 操作安卓串口_10


好了,当然还有很多不完善的地方,比如很多代码异常保护等等,大家可以自行增加,自行补充功能。本次讲解就到这。

QT部分其他功能大多都是这样了,大家有什么功能需要增加的,或者不懂的,可以留言,大家一起交流。

最终代码:

# ! /usr/bin/env python
# -*- coding: utf-8 -*-

import serial
import serial.tools.list_ports
import sys
from SerialPortTools import Ui_MainWindow
from PyQt5 import QtWidgets
import _thread

Com_Open_Flag = False  # 串口打开标志
COM_Band = {"9600", "115200"}
custom_serial = serial.Serial


# 获取串口列表
def Get_Com_List():
    return list(serial.tools.list_ports.comports())


# 获取串口数据
def Com_Data_Rsv(threadName):
    while True:
        while Com_Open_Flag:
            data = custom_serial.read_all()
            if data == b'':
                continue
            else:
                print("receive : ", data)
                window.Set_Display_Data(data)


# 界面UI按键程序重写
class Mywindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(Mywindow, self).__init__()
        self.setupUi(self)
        COM_List = Get_Com_List()  # 获取串口列表
        for i in range(0, len(COM_List)):  # 将列表导入到下拉框
            self.Com_Port.addItem(COM_List[i].name)
        self.Com_Band.addItem("9600")
        self.Com_Band.addItem("115200")

    def Open_Com_Click(self):
        global custom_serial  # 全局变量,需要加global
        global Com_Open_Flag
        print("点击了打开串口按钮")
        if self.Open_Com.text() == "打开串口":
            print(self.Com_Port.currentText())
            custom_serial = serial.Serial(self.Com_Port.currentText(), int(self.Com_Band.currentText()), timeout=0.5)
            if custom_serial.isOpen():
                print("open success")
                Com_Open_Flag = True
                self.Open_Com.setText("关闭串口")
                self.Com_Band.setEnabled(False)  # 串口号和波特率变为不可选择
                self.Com_Port.setEnabled(False)
            else:
                print("open failed")
        else:
            Com_Open_Flag = False
            self.Open_Com.setText("打开串口")
            custom_serial.close()
            self.Com_Band.setEnabled(True)  # 串口号和波特率变为可选择
            self.Com_Port.setEnabled(True)

    def Set_Display_Data(self, Data):
        self.Date_Display.insertPlainText(str(Data, encoding="utf-8"))

    def Send_Data_Click(self):
        print("点击了发送数据按钮")
        Data_Need_Send = self.Send_Data_Dsiplay.toPlainText()
        if custom_serial.isOpen():
            custom_serial.write(Data_Need_Send.encode("gbk"))
        else:
            print("请先打开串口")



if __name__ == '__main__':
  app = QtWidgets.QApplication(sys.argv)
  window = Mywindow()
  window.show()
  _thread.start_new_thread(Com_Data_Rsv, ("Thread-1",))
  sys.exit(app.exec_())