@DIY李小龙图标的串口调试助手!
Python有强大的serial库,可以直接使用,已通过串口指令测试控制步进电机,转速,转向,相对位移,更改波特率等操作。
安装的时候是pyserial, pycharm安装页面如下:
实际调用的时候是 serial
import serial #安装的是pyserial
先上一段测试视频:
Python制作的串口助手控制步进电机
DIY龙哥图标和图片
# 插入李小龙图片 ^_^
self.photo = PhotoImage(file='bruce.png')
self.photo_label = Label(self.init_window_name, image=self.photo)
self.photo_label.place(x=40, y=280)
也添加了自己的网址
# 加入超链接
def open_url(event):
webbrowser.open_new("https://www.stepping-motor.cn")
self.signature_label.bind("<Button-1>", open_url)
最后就是通过打包工具pyinstaller生成exe文件
pyinstaller -F -w -i logo.ico bruce.py
bruce.exe
拒绝套路,全部代码如下:
import tkinter
from tkinter import ttk, scrolledtext
from tkinter import *
import threading
import time
from datetime import datetime
import serial #安装的是pyserial
import serial.tools.list_ports
import re
import webbrowser
class MY_GUI():
# 构造函数
def __init__(self, name):
self.init_window_name = name
self.connected = False
# 窗口控件设置初始化
def set_init_window(self):
self.init_window_name.title('Bruce(ComHelper)')
self.init_window_name.geometry('740x620+220+10')
self.init_window_name.resizable(False, False)
self.init_window_name.attributes('-alpha', 1)
# 新添加icon
self.init_window_name.iconbitmap('wu.ico')
# 串口选择控制框架
self.com_choose_frame = Frame(self.init_window_name, width=10, height=100)
self.com_choose_frame.place(x=20, y=12)
# 1串口号标签
self.com_label = Label(self.com_choose_frame, text='Serial Port: ')
self.com_label.grid(row=1, column=0, sticky=W)
# 串口号下拉框
self.com_choose = StringVar()
self.com_choose_combo = ttk.Combobox(self.com_choose_frame, width=12, textvariable=self.com_choose)
self.com_choose_combo['state'] = 'readonly'
self.com_choose_combo.grid(row=1, column=1, sticky=W + E, padx=12)
self.com_choose_combo['values'] = self.com_name_get()
# 串口框架内部按钮
self.connect_button = Button(self.com_choose_frame, text='Open', bg='lightblue', width=12,
command=self.com_connect)
self.connect_button.grid(row=1, column=3, columnspan=2, padx=1, pady=4)
# 2波特率标签
self.baudrate_label = Label(self.com_choose_frame, text='Baud Rate: ')
self.baudrate_label.grid(row=2, column=0, sticky=W, padx=2)
# 波特率选项框
self.baudrate_value = StringVar(value='9600')
self.baudrate_choose_combo = ttk.Combobox(self.com_choose_frame, width=12, textvariable=self.baudrate_value)
self.baudrate_choose_combo['values'] = ('4800', '9600', '19200', '38400', '57600', '115200')
self.baudrate_choose_combo['state'] = 'readonly'
self.baudrate_choose_combo.grid(row=2, column=1, sticky=W, padx=12)
self.buttonN1 = Button(self.com_choose_frame, text='Confirm', bg='lightblue', width=12,
command=self.get_baudrate)
self.buttonN1.grid(row=2, column=3, columnspan=2, padx=1, pady=4)
# 3数据位标签
self.bytesize = Label(self.com_choose_frame, text='Byte Size: ')
self.bytesize.grid(row=3, column=0, sticky=W, padx=2)
# 数据位选项框
self.bytesize_value = StringVar(value='8')
self.bytesize_choose_combo = ttk.Combobox(self.com_choose_frame, width=12, textvariable=self.bytesize_value)
self.bytesize_choose_combo['values'] = ('5', '6', '7', '8')
self.bytesize_choose_combo['state'] = 'readonly'
self.bytesize_choose_combo.grid(row=3, column=1, padx=2)
self.buttonN2 = Button(self.com_choose_frame, text='Confirm', bg='lightblue', width=12, command=self.get_bytesize)
self.buttonN2.grid(row=3, column=3, columnspan=2, padx=1, pady=4)
# 4停止位标签
self.stopbits = Label(self.com_choose_frame, text='Stop Bits: ')
self.stopbits.grid(row=4, column=0, sticky=W, padx=2)
# 停止位选项框
self.stopbits_value = StringVar(value='1')
self.stopbits_choose_combo = ttk.Combobox(self.com_choose_frame, width=12, textvariable=self.stopbits_value)
self.stopbits_choose_combo['values'] = ('1', '1.5', '2')
self.stopbits_choose_combo['state'] = 'readonly'
self.stopbits_choose_combo.grid(row=4, column=1, padx=2)
self.buttonN3 = Button(self.com_choose_frame, text='Confirm', bg='lightblue', width=12, command=self.get_stopbits)
self.buttonN3.grid(row=4, column=3, columnspan=2, padx=1, pady=4)
# 5校验位标签
self.parity = Label(self.com_choose_frame, text='CRC: ')
self.parity.grid(row=5, column=0, sticky=W, padx=4)
# 校验位选项框
self.parity_value = StringVar(value='None')
self.parity_choose_combo = ttk.Combobox(self.com_choose_frame, width=12, textvariable=self.parity_value)
self.parity_choose_combo['values'] = ('None', 'Odd', 'Even', 'Mark', 'Space')
self.parity_choose_combo['state'] = 'readonly'
self.parity_choose_combo.grid(row=5, column=1, padx=2, pady=2)
self.buttonN1 = Button(self.com_choose_frame, text='Confirm', bg='lightblue', width=12, command=self.get_CRC)
self.buttonN1.grid(row=5, column=3, columnspan=2, padx=1, pady=4)
# 6 发送标签输入框和按钮
self.command_label = Label(self.com_choose_frame, text='Command: ')
self.command_label.grid(row=6, column=0, sticky=W, padx=4)
self.command = Entry(self.com_choose_frame, width=15)
self.command.insert(0, "ENA;")
self.command.grid(row=6, column=1, padx=2, pady=2)
self.command_send = Button(self.com_choose_frame, text='Send', bg='lightblue', width=12, command=self.com_send)
self.command_send.grid(row=6, column=3, columnspan=2, padx=1, pady=4)
# 插入签名,超链接和龙哥图片
self.signature_label = Label(self.init_window_name, text=' www.stepping-motor.cn', font=('Monospace', 14), cursor="hand2")
self.signature_label.place(x=20, y=580)
# 签名加入超链接
def open_url(event):
webbrowser.open_new("https://www.stepping-motor.cn")
self.signature_label.bind("<Button-1>", open_url)
# 插入李小龙图片 ^_^
self.photo = PhotoImage(file='bruce.png')
self.photo_label = Label(self.init_window_name, image=self.photo)
self.photo_label.place(x=40, y=280)
# 处理结果显示滚动文本框
self.result_data_label = Label(self.init_window_name, text=' Output & Result')
self.result_data_label.place(x=360, y=19)
self.clear_button = Button(self.init_window_name, text='Clear', bg='lightblue', width=12,
command=self.clear_result_text)
self.clear_button.place(x=480, y=14)
self.result_text = scrolledtext.ScrolledText(self.init_window_name, width=48, height=42)
self.result_text.place(x=360, y=50)
# 自动获取当前连接的串口名
def com_name_get(self):
self.port_list = list(serial.tools.list_ports.comports())
self.com_port_names = []
if len(self.port_list) > 0:
for i in range(len(self.port_list)):
self.com_name = str(self.port_list[i])
self.com_port_names.append(self.com_name)
return self.com_port_names
# 打开串口按键的执行内容
def com_connect(self):
if self.connected:
self.com_cancel()
return
self.ser_name = str(self.com_choose.get())
for i in range(len(self.port_list)):
if self.ser_name == str(self.port_list[i]):
self.ser_name, desc, hwid = self.port_list[i]
self.result_text.insert(END, f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}: connecting {self.ser_name}\n")
self.ser_baudrate = int(self.baudrate_value.get())
self.ser_bytesize = int(self.bytesize_value.get())
self.ser_stopbits = float(self.stopbits_value.get())
self.ser_parity = str(self.parity_value.get())[0:1]
try:
self.ser = serial.Serial(port=self.ser_name, baudrate=self.ser_baudrate, bytesize=self.ser_bytesize,
parity=self.ser_parity, stopbits=self.ser_stopbits)
self.ser.timeout = 0.01
self.result_text.insert(END, f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}: {self.ser_name} connected\n")
#参数显示下^_^
self.result_text.insert(END, f"Baud Rate: {self.get_baudrate()}\n")
self.result_text.insert(END, f"Byte Size: {self.get_bytesize()}\n")
self.result_text.insert(END, f"Stop Bits: {self.get_stopbits()}\n")
self.result_text.insert(END, f"CRC: {self.get_CRC()}\n")
self.result_text.see(tkinter.END)
self.result_text.update()
# 按钮变成“关闭串口”
self.connected = True
self.connect_button['text'] = 'Close'
# 开启接收串口数据线程
self.ReadUARTThread = threading.Thread(target=self.ReadUART, daemon=True)
self.ReadUARTThread.start()
except:
newline = f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}: {self.ser_name}connection failed\n"
self.result_text.insert(END, newline)
self.result_text.see(tkinter.END)
self.result_text.update()
# 关闭串口按键的执行内容
def com_cancel(self):
try:
self.ser.close()
# 按钮变成“打开串口”
self.connected = False
self.connect_button['text'] = 'Open'
except:
newline = time.ctime(time.time()) + ':' + "Serial Port is not connected" + '\n'
self.result_text.insert(END, newline)
self.result_text.see(tkinter.END)
self.result_text.update()
def clear_result_text(self):
self.result_text.delete('1.0', END)
def ReadUART(self):
try:
while self.connected:
newline = self.ser.readline() # 字节类型
if newline:
# print(newline)
self.result_text.insert(END,
f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}←{newline.decode('gbk')}\n")
self.result_text.see(tkinter.END)
self.result_text.update()
except:
# print(e)
newline = f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}: {self.ser_name} is closed\n"
self.result_text.insert(END, newline)
self.result_text.see(tkinter.END)
self.result_text.update()
# 确认波特率等参数
def get_baudrate(self):
return self.baudrate_value.get()
def get_bytesize(self):
return self.bytesize_value.get()
def get_stopbits(self):
return self.stopbits_value.get()
def get_CRC(self):
return self.parity_value.get()
def com_send(self):
data = self.command.get()
if data:
self.writeSerial(data)
def writeSerial(self, data):
try:
if self.connected and self.ser:
# print(data)
self.ser.write(data.encode('gbk'))
self.ser.flush()
newline = f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}→{data}\n"
self.result_text.insert(END, newline)
self.result_text.see(tkinter.END)
self.result_text.update()
except:
newline = f"{datetime.now().strftime('%H:%M:%S.%f')[:-3]}: {self.ser_name} data is not sent\n"
self.result_text.insert(END, newline)
self.result_text.see(tkinter.END)
self.result_text.update()
# 主线程
def start():
init_window = Tk()
my_window = MY_GUI(init_window)
my_window.set_init_window()
init_window.mainloop()
start()
# 打包生成exe : pyinstaller -F -w -i logo.ico bruce.py