文章目录

一、Pycharm1.环境配置2.镜像源配置3.工具配置(QtDesigner、PyUIC)

二、代码重要节点1.接收串口2.处理数据3.设计上位机前端4.处理上位机后端5.测试验证

一、Pycharm

如果用到Pycharm我们首先最关键的就是要知道Pycharm的Python环境

1.环境配置

Pycharm的环境配置在这里: 左上角->文件->setting(设置) 许多我们需要的函数及类都是在这里导入的,非常好用便捷。

2.镜像源配置

在左下角按照如图点击:我这里使用的是清华源 ↓

3.工具配置(QtDesigner、PyUIC)

左上角->文件->setting(设置)

接下来就是对应你下载的python里面的工具的路径:

二、代码重要节点

我们要清楚上位机的关键节点,下面就以我个人的节点及个人目的实际操作为例

1.接收串口

这里我想的一些功能包括: ①、可以打开一个或多个串口 ②、能够自动识别当前存在的串口号 所以我们首先引入两个库

import serial

import serial.tools.list_ports

我们来简单理解一下

my_ports = list(serial.tools.list_ports.comports())

if not my_ports:

print("当前无串口")

else:

for port in my_port:

print(list(port))

结果就会显示你当前存在的串口

接下来直奔主题现在我们尝试打开串口并接收串口传输的数据: 这里的串口数据最终会存到final_package。 数据协议要按照自己的协议去划分(我这里的是ti毫米波雷达demo的解析格式)。

class Process_data(QThread):

fin = pyqtSignal('PyQt_PyObject')

def __init__(self, com):

super().__init__()

self.ser = com

def run(self): # 重写QThread run函数

data = self.processing()

print(data)

self.fin.emit(data) # 信号槽 emit传递函数

def processing(self):

flag_Byte = bytearray(b'\x02\x01\x04\x03\x06\x05\x08\x07') # 当找到附合我们前8个字节的数据 我们才开始按照我们自己的逻辑去读与配置

this_Byte = self.ser.read(1)

final_package = bytearray(b'')

index = 0

while not this_Byte:

this_Byte = self.ser.read(1)

while 1: # 不断获取数据找符合我们的前八个字节逻辑的那一串数据

if this_Byte[0] == flag_Byte[index]: # 这里的[0]是不可以省的 因为flag_Byte[0]为 2 对应this_Byte为b'\x02' 不是2 不能划等号

index += 1

final_package.append(this_Byte[0])

if index == 8:

break

this_Byte = self.ser.read(1)

else:

if index == 0:

this_Byte = self.ser.read(1)

index = 0

final_package = bytearray(b'')

versionBytes = self.ser.read(4)

final_package += bytearray(versionBytes)

lengthBytes = self.ser.read(4)

final_package += bytearray(lengthBytes)

frameLength = int.from_bytes(lengthBytes, byteorder='little')

frameLength -= 16

final_package += bytearray(self.ser.read(frameLength))

return final_package

def recive_start():

recive.start(priority=QThread.HighestPriority)

def update_data(data):

print(data) #这里就是我们要显示在图上的数据了

thread_0 = QTimer()

thread_0.setSingleShot(False)

thread_0.timeout.connect(recive_start)

my_ports = [] # 在查找所有COM之前先初始化列表

ports = list(serial.tools.list_ports.comports()) # 获取串口list的长度

for com in ports:

my_ports.append(com[0]) # 添加刷新后得到的串口

if com[0] == 'COM16':

print('found')

index = my_ports.index(com[0])

# 这里我们可以加个checkbox 然后将my_ports 加到内部供选择,但这里为调试所以直接进入下一步

try:

ser = serial.Serial(my_ports[index], 921600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=0.08)

print('串口打开成功' + my_ports[index])

except:

print(my_ports[index])

print('串口打开失败')

if ser.is_open:

# 在这里我们要思考一个问题, 读取串口数据的同时 我们还要做的是去更新我们的前端显示,所以这里我们直接加个线程来为数据接收增加通道

recive = Process_data(ser)

print(recive.fin)

recive.fin.connect(update_data)

thread_0.start()

这段代码在执行时 会出现 1、 thread_0.start() 不执行 2、 Process_data中的run函数不执行 是因为代码要在PyQt应用程序上下文中执行。QTimer类是PyQt库的一部分,需要一个QApplication实例才能正常工作。确保在运行代码之前初始化了一个QApplication对象 我这里的执行结果为 found 串口打开成功COM16 bytearray(b’\x02\x01\x04\x03\x06\x05\x08\x07\x03\x01\x04\x07@\x00\x00\x00Ch\n\x00\xc86\x00\x00\x11Mv\xea\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x01\x04\x03\x06\x05\x08\x07\x03\x01\x04\x07@\x00\x00\x00Ch\n\x00\xc86\x00\x00’) 需要注意的是 里面没有\x的值是ASCII 需要手动转 Hex (\n是换行)

2.处理数据

既然收到数据,那就可以按照我们的协议来将我们需要的信息实例化,这就比较简单了,偏逻辑。我们这部分暂时先不做过多工作,上面说到要在QApplication实例中才能使用QTimer类,所以我们进入下一个步骤设计前端,实例QApplication,请看下一步。

3.设计上位机前端

打开我们前面新添加的外部工具QtDesigner. 随后思考自己所需要的控件,我需要: ①.一个单独布局用于放置可视化画布 ②.需要控制选择COM口 填写波特率等配置的功能框 简单的设计了一下,大家可按照自己的需求和审美加以改进。 随后另存到工程下面,使用external Tools 的 PyUIC功能即可生成python文件。

4.处理上位机后端

前端已经有了现在开始后端继续编写。

首先:引用我们生成的前端py文件。

import test_window #这里你的py文件叫什么你就些什么

随后:新增下面代码(如果你没有引入可以通过pycharm的提示来自动引入)

class window(QMainWindow, test_window.Ui_MainWindow): # 前面是QMainWindow方法 后面是我们生成的前端py文件的类名

def __init__(self):

super(window, self).__init__()

self.setupUi(self)

if __name__ == '__main__':

app = QtWidgets.QApplication(sys.argv)

window = window() #对应上面的方法

window.show()

sys.exit(app.exec_())

这里我们只运行上面两串的话就会出现我们刚才设计的前端页面。 最后:我们开始进行控件与语句的配合,使前端与我们前面设计的逻辑相匹配。

# 现在我们已经实例化pyqt了 所以我们重新将我们解析串口数据的函数主要部分修改为下面

class Process_data(QThread):

fin = pyqtSignal('PyQt_PyObject')

def __init__(self, com):

super().__init__()

self.ser = com

def run(self): # 重写QThread run函数

data = self.processing()

self.fin.emit(data) # 信号槽 emit传递函数

def processing(self):

flag_Byte = bytearray(b'\x02\x01\x04\x03\x06\x05\x08\x07') # 当找到附合我们前8个字节的数据 我们才开始按照我们自己的逻辑去读与配置

this_Byte = self.ser.read(1)

final_package = bytearray(b'')

index = 0

while not this_Byte:

this_Byte = self.ser.read(1)

while 1: # 不断获取数据找符合我们的前八个字节逻辑的那一串数据

if this_Byte[0] == flag_Byte[index]: # 这里的[0]是不可以省的 因为flag_Byte[0]为 2 对应this_Byte为b'\x02' 不是2 不能划等号

index += 1

final_package.append(this_Byte[0])

if index == 8:

break

this_Byte = self.ser.read(1)

else:

if index == 0:

this_Byte = self.ser.read(1)

index = 0

final_package = bytearray(b'')

versionBytes = self.ser.read(4)

final_package += bytearray(versionBytes)

lengthBytes = self.ser.read(4)

final_package += bytearray(lengthBytes)

frameLength = int.from_bytes(lengthBytes, byteorder='little')

frameLength -= 16

final_package += bytearray(self.ser.read(frameLength))

return final_package

class window(QMainWindow, test_window.Ui_MainWindow): # 前面是QMainWindow方法 后面是我们生成的前端py文件的类名

def __init__(self):

self.my_ports = [] # 在查找所有COM之前先初始化列表

super(window, self).__init__()

self.setupUi(self)

ports = list(serial.tools.list_ports.comports()) # 获取串口list的长度

# 此时我们已经有了复选框了 所以我们获取串口之后直接将其添加到复选框的选项中去

for com in ports:

self.my_ports.append(com[0]) # 添加刷新后得到的串口

# 这里我们可以加个checkbox 然后将my_ports 加到内部供选择,但这里为调试所以直接进入下一步

self.comboBox.addItems(self.my_ports)

self.thread_0 = QTimer()

self.thread_0.setSingleShot(False)

self.thread_0.timeout.connect(self.recive_start)

self.pushButton.clicked.connect(self.require_com)

self.pushButton_2.clicked.connect(self.start_com)

def require_com(self):

self.my_ports = []

self.comboBox.clear()

ports = list(serial.tools.list_ports.comports())

for com in ports:

self.my_ports.append(com[0])

self.comboBox.addItems(self.my_ports)

def recive_start(self):

self.recive.start(priority=QThread.HighestPriority)

def update_data(self, data): #这里就可以按照自己的串口数据去解析了

# hex_data = ' '.join(format(byte, '02x') for byte in data)

# print(hex_data) # 这里就是我们要显示在图上的数据了

if self.ser.is_open:

framelength = struct.unpack('I', data[12:16])[0]

frame = struct.unpack('I', data[20:24])[0]

det_num = struct.unpack('I', data[28:32])[0]

tlv_numflag = struct.unpack('I', data[32:36])[0]

index = 40

print(tlv_numflag)

for i in range(tlv_numflag):

tlvnum = struct.unpack('I', data[index:index + 4])[0]

if tlvnum == 1:

total = struct.unpack('I', data[index + 4:index + 8])[0]

self.deal_1(det_num, data, index)

index = total + 8

elif tlvnum == 2:

total = struct.unpack('I', data[index + 4:index + 8])[0]

index = total + 8

elif tlvnum == 3:

total = struct.unpack('I', data[index + 4:index + 8])[0]

# self.deal_2(det_num, data, index)

index = total + 8

def deal_1(self, num, data, index):

if num != 0:

print(index)

q = struct.unpack('h', data[index + 10:index + 12])[0]

print(q)

for i in range(num):

add = 12 + i * 10

x = (struct.unpack('h', data[index + add + 4:index + add + 6])[0]) / (2 ** q)

y = (struct.unpack('h', data[index + add + 6:index + add + 8])[0]) / (2 ** q)

z = (struct.unpack('h', data[index + add + 8:index + add + 10])[0]) / (2 ** q)

print(x)

print(y)

print(z)

print('---------------')

def deal_2(self, num, data, index):

print('1')

def start_com(self):

if self.pushButton_2.text() == '开始':

try:

self.ser = serial.Serial(self.comboBox.currentText(), self.lineEdit_2.text(), parity=serial.PARITY_NONE,

stopbits=serial.STOPBITS_ONE, timeout=0.08)

print('串口打开成功' + self.comboBox.currentText())

except:

QMessageBox.about(self, "出错", '串口打开失败')

if self.ser.is_open:

# 在这里我们要思考一个问题, 读取串口数据的同时 我们还要做的是去更新我们的前端显示,所以这里我们直接加个线程来为数据接收增加通道

self.recive = Process_data(self.ser)

self.recive.fin.connect(self.update_data)

self.thread_0.start()

self.pushButton_2.setText('关闭') # 点击开始之后 要相应把摁扭改为关闭

else:

self.ser.close()

self.pushButton_2.setText('开始')

if __name__ == '__main__':

app = QtWidgets.QApplication(sys.argv)

window = window()

window.show()

sys.exit(app.exec_())

5.测试验证

像我这边就是输出x y z

总之整体的节点流程已经完成了 其他就是自己设计自己想要的结果了,比如: 好了到这里就结束了。