main.py
import serial
import serial.tools.list_ports
import time
import tkinter as tk
from tkinter import ttk
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import threading
from queue import Queue
import LightSwitch
# 全局变量-----------------------------------------------------------------------
receivedata = [] # 接收数据列表
drawdata = [] # 画图列表
serialName = [] # 串口端口列表
ser = serial.Serial() # 串口实例对象
stop_event = threading.Event()
threadLock = threading.Lock()
data_queue = Queue()
# 函数和事件定义-----------------------------------------------------------------
# 接收和显示串口数据
def showdata():
global receivedata
while not stop_event.is_set():
if ser.is_open:
s = ser.read_all()
if s:
if var_Get.get():#Hex接收
try:
decoded_data = s.hex()
print(type(decoded_data),decoded_data)
if decoded_data:
with threadLock:
receivedata.append(decoded_data)
data_queue.put(decoded_data)
t0.insert('end', decoded_data)
t0.see('end') # 自动滚动到最新接收到的数据
except UnicodeDecodeError as e:
print(f"Error decoding data: {e}")
elif not var_Get.get():
try:
decoded_data = s.decode('utf-8')#将字节对象转换为字符串对象
print(type(decoded_data),decoded_data)
if decoded_data:
with threadLock:
receivedata.append(decoded_data)#将字符串对象添加到队列
data_queue.put(decoded_data)#通过队列在线程中传输数据
t0.insert('end', decoded_data)
t0.see('end') # 自动滚动到最新接收到的数据
except UnicodeDecodeError as e:
print(f"Error decoding data: {e}")
# 设置串口参数
def setPort(*args):
ser.port = comboxlist0.get()
def setbaudrate(*args):
ser.baudrate = int(comboxlist1.get())
def setShujv(*args):
ser.bytesize = int(comboxlist2.get())
check = {"N": serial.PARITY_NONE, "E": serial.PARITY_EVEN, "O": serial.PARITY_ODD}
def setcheck(*args):
ser.parity = check[comboxlist3.get()]
stop = {"1": serial.STOPBITS_ONE, "1.5": serial.STOPBITS_ONE_POINT_FIVE, "2": serial.STOPBITS_TWO}
def setstop(*args):
ser.stopbits = stop[comboxlist4.get()]
# 打开和关闭串口
def opencom():
global light_flag
if ser.is_open:
ser.close()
light.turn_off()
light_flag = 0
else:
ser.open()
light.turn_on()
light_flag = 1
# 发送数据
def send():
if not ser.is_open:
print("串口未打开")
return
elif var.get(): # Hex发送 单选框的值
demo = e0.get().strip() #发送输入文本框的值获取到并去掉 原始字符串的开头和结尾删除给定的字符默认为空格
demo = demo.replace(" ", "")
demo = demo.replace("0x", "")
if len(demo) % 2 != 0 and len(demo)>1:
demo = demo[:-1]+'0'+demo[-1]
try:
#demo[i:i+2]切片从i到i+2的2字符,将提取的2字符转换为16禁止的整数,int('48', 16) 会返回整数值 72,将所有转换后的整数转换为字节对象,hex_bytes为字节对象队列
hex_bytes = bytes(int(demo[i:i + 2], 16) for i in range(0, len(demo), 2))#i从0-len,步长2
ser.write(hex_bytes)#发送字节对象
print(f"Hex Bytes: {hex_bytes}")
except ValueError as e:
print(f"Error: {e}")
else: # 字符串发送
demo = e0.get().strip()#获取文本框值,并去掉空格和换行,并将其编码为字节序列
demo = demo.replace(" ", "")
demo = demo.encode()
ser.write(demo)
# 清除接收数据
def clearreceive():
with threadLock:
receivedata.clear()
t0.delete(1.0, 'end')
# 单选框选择 - Hex发送
def selectionvar():
global on_hit
on_hit = var.get()
#单选框选择 - Hex接收
def selectionvarGet():
global on_hit
on_hit = var.get()
# 停止所有线程
def Stop_EVEN():
stop_event.set()
time.sleep(1)
th0.join()
#th1.join()
ser.close()
window.quit()
# 初始化窗口和控件
window = tk.Tk()
window.title('chenyuean的串口助手')
window.geometry('900x600+200+100')
# 获取可用串口列表
port_list = list(serial.tools.list_ports.comports())
for port in port_list:
serialName.append(port[0])
# 创建文本控件和下拉列表控件
label0 = tk.Label(window, text='串口', font=10)
label0.place(y=50, x=50)
label1 = tk.Label(window, text='波特率', font=10)
label1.place(y=100, x=50)
label2 = tk.Label(window, text='数据位', font=10)
label2.place(y=150, x=50)
label3 = tk.Label(window, text='校验位', font=10)
label3.place(y=200, x=50)
label4 = tk.Label(window, text='停止位', font=10)
label4.place(y=250, x=50)
#端口号下拉框
comvalue0 = tk.StringVar()
comboxlist0 = ttk.Combobox(window, textvariable=comvalue0)
comboxlist0["values"] = tuple(serialName)
comboxlist0.bind("<<ComboboxSelected>>", setPort)
comboxlist0.place(y=50, x=150)
#波特率下拉框
comvalue1 = tk.StringVar()
comboxlist1 = ttk.Combobox(window, textvariable=comvalue1)
comboxlist1["values"] = ("1200", "2400", "4800", "9600", "14400", "19200", "115200", "256000")
comboxlist1.current(6)
comboxlist1.bind("<<ComboboxSelected>>", setbaudrate)
comboxlist1.place(y=100, x=150)
#数据位下拉框
comvalue2 = tk.StringVar()
comboxlist2 = ttk.Combobox(window, textvariable=comvalue2)
comboxlist2["values"] = ("8", "7", "6", "5")
comboxlist2.current(0)
comboxlist2.bind("<<ComboboxSelected>>", setShujv)
comboxlist2.place(y=150, x=150)
#校验位下拉框
comvalue3 = tk.StringVar()
comboxlist3 = ttk.Combobox(window, textvariable=comvalue3)
comboxlist3["values"] = ("N", "E", "O")
comboxlist3.current(0)
comboxlist3.bind("<<ComboboxSelected>>", setcheck)
comboxlist3.place(y=200, x=150)
#停止位下拉框
comvalue4 = tk.StringVar()
comboxlist4 = ttk.Combobox(window, textvariable=comvalue4)
comboxlist4["values"] = ("1", "1.5", "2")
comboxlist4.current(0)
comboxlist4.bind("<<ComboboxSelected>>", setstop)
comboxlist4.place(y=250, x=150)
b0 = tk.Button(window, text='打开串口', width=35, height=1, command=opencom)
b0.place(y=300, x=50)
stop_EV = tk.Button(window, text='关闭线程和程序', width=20, height=1, font=2, command=Stop_EVEN)
stop_EV.place(y=0, x=0)
light = LightSwitch.BooleanLight(window, size=35, x=10, y=295)#串口连接状态显示灯
btn_turn_on = tk.Button(window, text="Turn On", command=light.turn_on)
b1 = tk.Button(window, text='清除接收', width=35, height=1, command=clearreceive)
b1.place(y=350, x=50)
#发送输入文本框
e0 = tk.Entry(window, show=None, width=35)
e0.place(y=400, x=50)
#输入文本框Hex选择框
var = tk.IntVar(value=1)
c1 = tk.Checkbutton(window, text='Hex发送', variable=var, onvalue=1, offvalue=0, command=selectionvar)
c1.place(y=450, x=50)
#发送按钮
b1 = tk.Button(window, text='Send', width=20, height=1, command=send)
b1.place(y=450, x=150)
#接收文本框Hex选择框
var_Get = tk.IntVar(value=1)
c1 = tk.Checkbutton(window, text='Hex接收', variable=var_Get, onvalue=1, offvalue=0, command=selectionvarGet)
c1.place(y=0, x=350)
#接收显示
t0 = tk.Text(window, width=65, height=2)
t0.place(y=50, x=350)
label5 = tk.Label(window, text='Canvas', font=10)
label5.place(y=100, x=350)
# 图像参数
canvas = tk.Canvas(window,width=320, height=240,bg='#{:02x}{:02x}{:02x}'.format(*(255,255,255)),bd=1,highlightthickness=1,relief="groove")
canvas.pack()
canvas.place(x=420, y=125)
if __name__ == '__main__':
stop_event = threading.Event()#等待线程停止事件
th0 = threading.Thread(target=showdata)
th0.start()
# th1 = threading.Thread(target=startdraw)
# th1.start()
window.mainloop()
LightSwitch.py
import tkinter as tk
class BooleanLight(tk.Canvas):
def __init__(self, master=None, size=50, x=0, y=0):
super().__init__(master, width=size, height=size)
self.pack()
self.place(x=x, y=y)
self.size = size
self.is_light_on = False # 初始状态为关闭
# 创建初始的圆形表示灯的状态
self.light = self.create_oval(5, 5, size - 5, size - 5, fill="gray")
def turn_on(self):
"""打开灯"""
self.itemconfig(self.light, fill="green")
self.is_light_on = True
def turn_off(self):
"""关闭灯"""
self.itemconfig(self.light, fill="gray")
self.is_light_on = False
def toggle(self):
"""切换灯的状态"""
if self.is_light_on:
self.turn_off()
else:
self.turn_on()
可完成功能,接收文本数据和Hex数据,发送文本数据和Hex数据