需要与串口设备进行通讯,那么一个调试工具是必须的。
根据我自己的需要,写了个简易版本的串口调试工具:
预览图:
======================
项目结构:
COM
--SerialHelper.py
UI
--Adaptive.py
--SerialTool.py
--PyTkinter.py
main.py
======================
COM文件夹
SerialHelper.py 串口通讯帮助类
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 ''' 5 Serial设备通讯帮助类 6 ''' 7 __author__ = "jakey.chen" 8 __version__ = "v1.0" 9 10 import sys 11 import threading 12 import time 13 import serial 14 import binascii 15 import logging 16 17 class SerialHelper(object): 18 def __init__(self, Port="COM6", BaudRate="9600", ByteSize="8", Parity="N", Stopbits="1"): 19 ''' 20 初始化一些参数 21 ''' 22 self.l_serial = None 23 self.alive = False 24 self.port = Port 25 self.baudrate = BaudRate 26 self.bytesize = ByteSize 27 self.parity = Parity 28 self.stopbits = Stopbits 29 self.thresholdValue = 64 30 self.receive_data = "" 31 32 def start(self): 33 ''' 34 开始,打开串口 35 ''' 36 self.l_serial = serial.Serial() 37 self.l_serial.port = self.port 38 self.l_serial.baudrate = self.baudrate 39 self.l_serial.bytesize = int(self.bytesize) 40 self.l_serial.parity = self.parity 41 self.l_serial.stopbits = int(self.stopbits) 42 self.l_serial.timeout = 2 43 44 try: 45 self.l_serial.open() 46 if self.l_serial.isOpen(): 47 self.alive = True 48 except Exception as e: 49 self.alive = False 50 logging.error(e) 51 52 def stop(self): 53 ''' 54 结束,关闭串口 55 ''' 56 self.alive = False 57 if self.l_serial.isOpen(): 58 self.l_serial.close() 59 60 def read(self): 61 ''' 62 循环读取串口发送的数据 63 ''' 64 while self.alive: 65 try: 66 number = self.l_serial.inWaiting() 67 if number: 68 self.receive_data += self.l_serial.read(number).replace(binascii.unhexlify("00"), "") 69 if self.thresholdValue <= len(self.receive_data): 70 self.receive_data = "" 71 except Exception as e: 72 logging.error(e) 73 74 def write(self, data, isHex=False): 75 ''' 76 发送数据给串口设备 77 ''' 78 if self.alive: 79 if self.l_serial.isOpen(): 80 if isHex: 81 # data = data.replace(" ", "").replace("\n", "") 82 data = binascii.unhexlify(data) 83 self.l_serial.write(data) 84 85 if __name__ == '__main__': 86 import threading 87 ser = SerialHelper() 88 ser.start() 89 90 ser.write("123", isHex=False) 91 thread_read = threading.Thread(target=ser.read) 92 thread_read.setDaemon(True) 93 thread_read.start() 94 import time 95 time.sleep(25) 96 ser.stop()
======================
UI文件夹
Adaptive.py 防止错位
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import platform 5 6 g_systemName = platform.system() 7 g_systemInfo = platform.platform() 8 g_pyVersion = platform.python_version() 9 size_dict = dict() 10 11 # System will be Linux and python == 2.7 12 if g_systemName == "Linux" and g_pyVersion[:3] == "2.7": 13 if "Ubuntu" in g_systemInfo: 14 size_dict = { 15 "list_box_height": 20, 16 "send_text_height": 12, 17 "receive_text_height": 15, 18 "reset_label_width": 24, 19 "clear_label_width": 22 20 } 21 22 # raspberry pi 23 elif "armv6l" in g_systemInfo: 24 size_dict = { 25 "list_box_height": 19, 26 "send_text_height": 12, 27 "receive_text_height": 15, 28 "reset_label_width": 24, 29 "clear_label_width": 22 30 } 31 else: 32 if g_systemInfo[:9]== "Windows-8": 33 size_dict = { 34 "list_box_height": 14, 35 "send_text_height": 6, 36 "receive_text_height": 18, 37 "reset_label_width": 7, 38 "clear_label_width": 5 39 } 40 41 elif g_systemInfo[:9]== "Windows-7": 42 size_dict = { 43 "list_box_height": 13, 44 "send_text_height": 12, 45 "receive_text_height": 15, 46 "reset_label_width": 7, 47 "clear_label_width": 5 48 } 49 50 elif g_systemInfo[:10]== "Windows-XP": 51 size_dict = { 52 "list_box_height": 20, 53 "send_text_height": 12, 54 "receive_text_height": 22, 55 "reset_label_width": 7, 56 "clear_label_width": 5 57 } 58 59 # font 60 monaco_font = ('Monaco', 12)
PyTkinter.py 根据个人喜好来初始化一些Tkinter控件的颜色配置
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 ''' 5 Tkinter控件初始化配置(默认为深色) 6 ''' 7 __author__ = "jakey.chen" 8 __version__ = "v1.0" 9 10 11 import Tkinter as tk 12 13 g_default_theme = "dark" 14 # g_default_theme = "default" 15 16 class PyButton(tk.Button): 17 ''' 18 Button 19 ''' 20 def __init__(self, master, theme=g_default_theme, **kv): 21 self.theme = theme 22 self.kv = kv 23 self.temp = dict() 24 self.choose_theme() 25 tk.Button.__init__(self, master, self.temp) 26 27 def choose_theme(self): 28 if self.theme == "dark": 29 dark_theme_dict = { 30 "activebackground": "#00B2EE", 31 "activeforeground": "#E0EEEE", 32 "bg": "#008B8B", 33 "fg": "#FFFFFF" 34 } 35 for key,value in dark_theme_dict.items(): 36 self.temp[key] = value 37 38 for key,value in self.kv.items(): 39 self.temp[key] = value 40 41 class PyLabel(tk.Label): 42 ''' 43 Label 44 ''' 45 def __init__(self, master, theme=g_default_theme, **kv): 46 self.theme = theme 47 self.kv = kv 48 self.temp = dict() 49 self.choose_theme() 50 tk.Label.__init__(self, master, self.temp) 51 52 def choose_theme(self): 53 if self.theme == "dark": 54 dark_theme_dict = { 55 "bg": "#292929", 56 "fg": "#E0EEEE" 57 } 58 for key,value in dark_theme_dict.items(): 59 self.temp[key] = value 60 61 for key,value in self.kv.items(): 62 self.temp[key] = value 63 64 class PyLabelFrame(tk.LabelFrame): 65 ''' 66 Frame 67 ''' 68 def __init__(self, master, theme=g_default_theme, **kv): 69 self.theme = theme 70 self.kv = kv 71 self.temp = dict() 72 self.choose_theme() 73 tk.LabelFrame.__init__(self, master, self.temp) 74 75 def choose_theme(self): 76 if self.theme == "dark": 77 dark_theme_dict = { 78 "bg": "#292929", 79 "fg": "#1E90FF" 80 } 81 for key,value in dark_theme_dict.items(): 82 self.temp[key] = value 83 84 for key,value in self.kv.items(): 85 self.temp[key] = value 86 87 class PyListbox(tk.Listbox): 88 ''' 89 Listbox 90 ''' 91 def __init__(self, master, theme=g_default_theme, **kv): 92 self.theme = theme 93 self.kv = kv 94 self.temp = dict() 95 self.choose_theme() 96 tk.Listbox.__init__(self, master, self.temp) 97 98 def choose_theme(self): 99 if self.theme == "dark": 100 dark_theme_dict = { 101 "bg": "#292929", 102 "fg": "#1E90FF", 103 "selectbackground": "#00B2EE" 104 } 105 for key,value in dark_theme_dict.items(): 106 self.temp[key] = value 107 108 for key,value in self.kv.items(): 109 self.temp[key] = value 110 111 class PyText(tk.Text): 112 ''' 113 Text 114 ''' 115 def __init__(self, master, theme=g_default_theme, **kv): 116 self.theme = theme 117 self.kv = kv 118 self.temp = dict() 119 self.choose_theme() 120 tk.Text.__init__(self, master, self.temp) 121 122 def choose_theme(self): 123 if self.theme == "dark": 124 dark_theme_dict = { 125 "bg": "#292929", 126 "fg": "#1E90FF" 127 } 128 for key,value in dark_theme_dict.items(): 129 self.temp[key] = value 130 131 for key,value in self.kv.items(): 132 self.temp[key] = value 133 134 class PyCheckbutton(tk.Checkbutton): 135 ''' 136 Checkbutton 137 ''' 138 def __init__(self, master, theme=g_default_theme, **kv): 139 self.theme = theme 140 self.kv = kv 141 self.temp = dict() 142 self.choose_theme() 143 tk.Checkbutton.__init__(self, master, self.temp) 144 145 def choose_theme(self): 146 if self.theme == "dark": 147 dark_theme_dict = { 148 "bg": "#292929", 149 "fg": "#FFFFFF", 150 "activebackground": "#292929", 151 "activeforeground": "#FFFFFF", 152 "selectcolor": "#292929" 153 } 154 for key,value in dark_theme_dict.items(): 155 self.temp[key] = value 156 157 for key,value in self.kv.items(): 158 self.temp[key] = value 159 160 class PyRadiobutton(tk.Radiobutton): 161 ''' 162 Radiobutton 163 ''' 164 def __init__(self, master, theme=g_default_theme, **kv): 165 self.theme = theme 166 self.kv = kv 167 self.temp = dict() 168 self.choose_theme() 169 tk.Radiobutton.__init__(self, master, self.temp) 170 171 def choose_theme(self): 172 if self.theme == "dark": 173 dark_theme_dict = { 174 "bg": "#292929", 175 "fg": "#FFFFFF", 176 "activebackground": "#292929", 177 "selectcolor": "#292929" 178 } 179 for key,value in dark_theme_dict.items(): 180 self.temp[key] = value 181 182 for key,value in self.kv.items(): 183 self.temp[key] = value 184 185 186 class PyEntry(tk.Entry): 187 ''' 188 Entry 189 ''' 190 def __init__(self, master, theme=g_default_theme, **kv): 191 self.theme = theme 192 self.kv = kv 193 self.temp = dict() 194 self.choose_theme() 195 tk.Entry.__init__(self, master, self.temp) 196 197 def choose_theme(self): 198 if self.theme == "dark": 199 dark_theme_dict = { 200 "bg": "#292929", 201 "fg": "#E0EEEE", 202 "insertbackground": "#E0EEEE" 203 } 204 for key,value in dark_theme_dict.items(): 205 self.temp[key] = value 206 207 for key,value in self.kv.items(): 208 self.temp[key] = value 209 210 if __name__ == '__main__': 211 root = tk.Tk() 212 root.configure(bg="#292929") 213 PyButton(root, text="1234", font=("Monaco", 12)).pack() 214 PyLabel(root, text="123", font=("Monaco", 15)).pack() 215 PyCheckbutton(root, text="123", font=("Monaco", 15)).pack() 216 PyEntry(root, font=("Monaco", 15)).pack() 217 PyText(root, font=("Monaco", 15), height=2, width=20).pack() 218 listbox_0 = PyListbox(root, height=2, font=("Monaco", 15)) 219 listbox_0.pack() 220 for i in range(2): 221 listbox_0.insert("end", i) 222 radio_intvar = tk.IntVar() 223 PyRadiobutton(root, text="001", variable=radio_intvar, value=0, font=("Monaco", 15)).pack() 224 PyRadiobutton(root, text="002", variable=radio_intvar, value=1, font=("Monaco", 15)).pack() 225 radio_intvar.set(1) 226 227 root.mainloop()
SerialTool.py 主界面
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import Tkinter as tk 5 import ttk 6 import PyTkinter as pytk 7 import Adaptive 8 9 font = Adaptive.monaco_font 10 size_dict = Adaptive.size_dict 11 g_default_theme = pytk.g_default_theme 12 13 14 class SerialToolUI(object): 15 def __init__(self, master=None): 16 self.root = master 17 self.create_frame() 18 self.thresholdValue = 1 19 20 def create_frame(self): 21 ''' 22 新建窗口,分为上下2个部分,下半部分为状态栏 23 ''' 24 self.frm = pytk.PyLabelFrame(self.root) 25 self.frm_status = pytk.PyLabelFrame(self.root) 26 27 self.frm.grid(row=0, column=0, sticky="wesn") 28 self.frm_status.grid(row=1, column=0, sticky="wesn") 29 30 self.create_frm() 31 self.create_frm_status() 32 33 def create_frm(self): 34 ''' 35 上半部分窗口分为左右2个部分 36 ''' 37 self.frm_left = pytk.PyLabelFrame(self.frm) 38 self.frm_right = pytk.PyLabelFrame(self.frm) 39 40 self.frm_left.grid(row=0, column=0, padx=5, pady=5, sticky="wesn") 41 self.frm_right.grid(row=0, column=1, padx=5, pady=5, sticky="wesn") 42 43 self.create_frm_left() 44 self.create_frm_right() 45 46 def create_frm_left(self): 47 ''' 48 上半部分左边窗口: 49 Listbox显示可用的COM口 50 Button按钮点击连接设备 51 ''' 52 self.frm_left_label = pytk.PyLabel(self.frm_left, 53 text="Serial Ports", 54 font=font) 55 self.frm_left_listbox = pytk.PyListbox(self.frm_left, 56 height=size_dict["list_box_height"], 57 font=font) 58 self.frm_left_serial_set = pytk.PyLabelFrame(self.frm_left) 59 self.frm_left_btn = pytk.PyButton(self.frm_left, 60 text="Open", 61 font=font, 62 command=self.Toggle) 63 64 self.frm_left_label.grid(row=0, column=0, padx=5, pady=5, sticky="w") 65 self.frm_left_listbox.grid(row=1, column=0, padx=5, pady=5, sticky="wesn") 66 self.frm_left_serial_set.grid(row=2, column=0, padx=5, pady=5, sticky="wesn") 67 self.frm_left_btn.grid(row=3, column=0, padx=5, pady=5, sticky="wesn") 68 69 self.frm_left_listbox.bind("<Double-Button-1>", self.Open) 70 self.create_frm_left_serial_set() 71 72 def create_frm_left_serial_set(self): 73 ''' 74 串口配置,比如波特率,奇偶校验等 75 ''' 76 setting_label_list = ["BaudRate :", "Parity :", "DataBit :", "StopBit :"] 77 baudrate_list = ["1200", "2400", "4800", "9600", "14400", "19200", "38400", 78 "43000", "57600", "76800", "115200", "12800"] 79 # PARITY_NONE, PARITY_EVEN, PARITY_ODD PARITY_MARK, PARITY_SPACE 80 parity_list = ["N", "E", "O", "M", "S"] 81 bytesize_list = ["5", "6", "7", "8"] 82 stopbits_list = ["1", "1.5", "2"] 83 for index,item in enumerate(setting_label_list): 84 frm_left_label_temp = pytk.PyLabel(self.frm_left_serial_set, 85 text=item, 86 font=('Monaco', 10)) 87 frm_left_label_temp.grid(row=index, column=0, padx=1, pady=2, sticky="e") 88 self.frm_left_combobox_baudrate = ttk.Combobox(self.frm_left_serial_set, 89 width=15, 90 values=baudrate_list) 91 self.frm_left_combobox_parity = ttk.Combobox(self.frm_left_serial_set, 92 width=15, 93 values=parity_list) 94 self.frm_left_combobox_databit = ttk.Combobox(self.frm_left_serial_set, 95 width=15, 96 values=bytesize_list) 97 self.frm_left_combobox_stopbit = ttk.Combobox(self.frm_left_serial_set, 98 width=15, 99 values=stopbits_list) 100 self.frm_left_combobox_baudrate.grid(row=0, column=1, padx=2, pady=2, sticky="e") 101 self.frm_left_combobox_parity.grid(row=1, column=1, padx=2, pady=2, sticky="e") 102 self.frm_left_combobox_databit.grid(row=2, column=1, padx=2, pady=2, sticky="e") 103 self.frm_left_combobox_stopbit.grid(row=3, column=1, padx=2, pady=2, sticky="e") 104 105 self.frm_left_combobox_baudrate.current(3) 106 self.frm_left_combobox_parity.current(0) 107 self.frm_left_combobox_databit.current(3) 108 self.frm_left_combobox_stopbit.current(0) 109 110 def create_frm_right(self): 111 ''' 112 上半部分右边窗口: 113 分为4个部分: 114 1、Label显示和重置按钮和发送按钮 115 2、Text显示(发送的数据) 116 3、Label显示和十六进制选择显示和清除接收信息按钮 117 4、Text显示接收到的信息 118 ''' 119 self.frm_right_reset = pytk.PyLabelFrame(self.frm_right) 120 self.frm_right_send = pytk.PyText(self.frm_right, 121 width=50, 122 height=size_dict["send_text_height"], 123 font=("Monaco", 9)) 124 self.frm_right_clear = pytk.PyLabelFrame(self.frm_right) 125 self.frm_right_receive = pytk.PyText(self.frm_right, 126 width=50, 127 height=size_dict["receive_text_height"], 128 font=("Monaco", 9)) 129 130 self.frm_right_reset.grid(row=0, column=0, padx=1, sticky="wesn") 131 self.frm_right_send.grid(row=1, column=0, padx=1, sticky="wesn") 132 self.frm_right_clear.grid(row=2, column=0, padx=1, sticky="wesn") 133 self.frm_right_receive.grid(row=3, column=0, padx=1, sticky="wesn") 134 135 self.frm_right_receive.tag_config("green", foreground="#228B22") 136 137 self.create_frm_right_reset() 138 self.create_frm_right_clear() 139 140 def create_frm_right_reset(self): 141 ''' 142 1、Label显示和重置按钮和发送按钮 143 ''' 144 self.frm_right_reset_label = pytk.PyLabel(self.frm_right_reset, 145 text="Data Send" + " "*size_dict["reset_label_width"], 146 font=font) 147 self.new_line_cbtn_var = tk.IntVar() 148 self.send_hex_cbtn_var = tk.IntVar() 149 self.frm_right_reset_newLine_checkbtn = pytk.PyCheckbutton(self.frm_right_reset, 150 text="New Line", 151 variable=self.new_line_cbtn_var, 152 font=font) 153 self.frm_right_reset_hex_checkbtn = pytk.PyCheckbutton(self.frm_right_reset, 154 text="Hex", 155 variable=self.send_hex_cbtn_var, 156 font=font) 157 self.frm_right_reset_btn = pytk.PyButton(self.frm_right_reset, 158 text="Reset", 159 width=10, 160 font=font, 161 command=self.Reset) 162 self.frm_right_send_btn = pytk.PyButton(self.frm_right_reset, 163 text="Send", 164 width=10, 165 font=font, 166 command=self.Send) 167 168 self.frm_right_reset_label.grid(row=0, column=0, sticky="w") 169 self.frm_right_reset_newLine_checkbtn.grid(row=0, column=1, sticky="wesn") 170 self.frm_right_reset_hex_checkbtn.grid(row=0, column=2, sticky="wesn") 171 self.frm_right_reset_btn.grid(row=0, column=3, padx=5, pady=5, sticky="wesn") 172 self.frm_right_send_btn.grid(row=0, column=4, padx=5, pady=5, sticky="wesn") 173 174 def create_frm_right_clear(self): 175 ''' 176 3、Label显示和十六进制显示和清除接收信息按钮 177 ''' 178 self.receive_hex_cbtn_var = tk.IntVar() 179 self.frm_right_clear_label = pytk.PyLabel(self.frm_right_clear, 180 text="Data Received"+ " "*size_dict["clear_label_width"], 181 font=font) 182 self.frm_right_threshold_label = pytk.PyLabel(self.frm_right_clear, 183 text="Threshold:", 184 font=font) 185 self.thresholdStr = tk.StringVar() 186 self.frm_right_threshold_entry = pytk.PyEntry(self.frm_right_clear, 187 textvariable=self.thresholdStr, 188 width=6, 189 font=font) 190 self.frm_right_hex_checkbtn = pytk.PyCheckbutton(self.frm_right_clear, 191 text="Hex", 192 variable=self.receive_hex_cbtn_var, 193 relief="flat", 194 font=font) 195 self.frm_right_clear_btn = pytk.PyButton(self.frm_right_clear, 196 text="Clear", 197 width=10, 198 font=font, 199 command=self.Clear) 200 201 self.frm_right_clear_label.grid(row=0, column=0, padx=5, pady=5, sticky="w") 202 self.frm_right_threshold_label.grid(row=0, column=1, padx=5, pady=5, sticky="wesn") 203 self.frm_right_threshold_entry.grid(row=0, column=2, padx=5, pady=5, sticky="wesn") 204 self.frm_right_hex_checkbtn.grid(row=0, column=3, padx=5, pady=5, sticky="wesn") 205 self.frm_right_clear_btn.grid(row=0, column=4, padx=5, pady=5, sticky="wesn") 206 207 self.thresholdStr.set(1) 208 self.thresholdStr.trace('w', self.GetThresholdValue) 209 210 def create_frm_status(self): 211 ''' 212 下半部分状态栏窗口 213 ''' 214 self.frm_status_label = pytk.PyLabel(self.frm_status, 215 text="Ready", 216 font=font) 217 self.frm_status_label.grid(row=0, column=0, padx=5, pady=5, sticky="wesn") 218 219 def Toggle(self): 220 pass 221 222 def Open(self, event): 223 pass 224 225 def Reset(self): 226 self.frm_right_send.delete("0.0", "end") 227 228 def Send(self): 229 pass 230 231 def Clear(self): 232 self.frm_right_receive.delete("0.0", "end") 233 234 def GetThresholdValue(self, *args): 235 try: 236 self.thresholdValue = int(self.thresholdStr.get()) 237 except: 238 pass 239 240 241 if __name__ == '__main__': 242 ''' 243 main loop 244 ''' 245 root = tk.Tk() 246 if g_default_theme == "dark": 247 root.configure(bg="#292929") 248 combostyle = ttk.Style() 249 combostyle.theme_use('alt') 250 combostyle.configure("TCombobox", selectbackground="#292929", fieldbackground="#292929", 251 background="#292929", foreground="#FFFFFF") 252 root.title("Serial-Tool") 253 SerialToolUI(master=root) 254 root.resizable(False, False) 255 root.mainloop()
======================
界面逻辑主程序
main.py
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 import time 5 import datetime 6 import threading 7 import binascii 8 import platform 9 import logging 10 11 from UI import SerialTool 12 from COM import SerialHelper 13 14 if platform.system() == "Windows": 15 from serial.tools import list_ports 16 elif platform.system() == "Linux": 17 import glob, os, re 18 19 import Tkinter as tk 20 import ttk 21 22 logging.basicConfig(level=logging.DEBUG, 23 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', 24 datefmt='%a, %d %b %Y %H:%M:%S') 25 26 class MainSerialToolUI(SerialTool.SerialToolUI): 27 def __init__(self, master=None): 28 super(MainSerialToolUI, self).__init__() 29 self.ser = None 30 self.receive_count = 0 31 self.receive_data = "" 32 self.list_box_serial = list() 33 self.find_all_serial() 34 35 def __del__(self): 36 if platform.system() == "Linux": 37 try: 38 self.ser.SetStopEvent() 39 except: 40 pass 41 42 def find_all_serial(self): 43 ''' 44 获取到串口列表 45 ''' 46 if platform.system() == "Windows": 47 try: 48 self.temp_serial = list() 49 for com in list_ports.comports(): 50 strCom = com[0] + ": " + com[1][:-7].decode("gbk").encode("utf-8") 51 self.temp_serial.append(strCom) 52 for item in self.temp_serial: 53 if item not in self.list_box_serial: 54 self.frm_left_listbox.insert("end", item) 55 for item in self.list_box_serial: 56 if item not in self.temp_serial: 57 index = list(self.frm_left_listbox.get(0, self.frm_left_listbox.size())).index(item) 58 self.frm_left_listbox.delete(index) 59 60 self.list_box_serial = self.temp_serial 61 62 self.thread_findserial = threading.Timer(1, self.find_all_serial) 63 self.thread_findserial.setDaemon(True) 64 self.thread_findserial.start() 65 except Exception as e: 66 logging.error(e) 67 elif platform.system() == "Linux": 68 try: 69 self.temp_serial = list() 70 self.temp_serial = self.find_usb_tty() 71 for item in self.temp_serial: 72 if item not in self.list_box_serial: 73 self.frm_left_listbox.insert("end", item) 74 for item in self.list_box_serial: 75 if item not in self.temp_serial: 76 index = list(self.frm_left_listbox.get(0, self.frm_left_listbox.size())).index(item) 77 self.frm_left_listbox.delete(index) 78 self.list_box_serial = self.temp_serial 79 80 self.thread_findserial = threading.Timer(1, self.find_all_serial) 81 self.thread_findserial.setDaemon(True) 82 self.thread_findserial.start() 83 except Exception as e: 84 logging.error(e) 85 86 def Toggle(self): 87 ''' 88 打开关闭串口 89 ''' 90 if self.frm_left_btn["text"] == "Open": 91 try: 92 self.currentStrCom = self.frm_left_listbox.get(self.frm_left_listbox.curselection()) 93 if platform.system() == "Windows": 94 self.port = self.currentStrCom.split(":")[0] 95 elif platform.system() == "Linux": 96 self.port = self.currentStrCom 97 self.baudrate = self.frm_left_combobox_baudrate.get() 98 self.parity = self.frm_left_combobox_parity.get() 99 self.databit = self.frm_left_combobox_databit.get() 100 self.stopbit = self.frm_left_combobox_stopbit.get() 101 self.ser = SerialHelper.SerialHelper(Port=self.port, 102 BaudRate=self.baudrate, 103 ByteSize=self.databit, 104 Parity=self.parity, 105 Stopbits=self.stopbit) 106 self.ser.start() 107 if self.ser.alive: 108 self.frm_status_label["text"] = "Open [{0}] Successful!".format(self.currentStrCom) 109 self.frm_status_label["fg"] = "#66CD00" 110 self.frm_left_btn["text"] = "Close" 111 self.frm_left_btn["bg"] = "#F08080" 112 113 self.thread_read = threading.Thread(target=self.SerialRead) 114 self.thread_read.setDaemon(True) 115 self.thread_read.start() 116 117 except Exception as e: 118 logging.error(e) 119 try: 120 self.frm_status_label["text"] = "Open [{0}] Failed!".format(self.currentStrCom) 121 self.frm_status_label["fg"] = "#DC143C" 122 except Exception as ex: 123 logging.error(ex) 124 125 elif self.frm_left_btn["text"] == "Close": 126 try: 127 self.ser.stop() 128 self.receive_count = 0 129 except Exception as e: 130 logging.error(e) 131 self.frm_left_btn["text"] = "Open" 132 self.frm_left_btn["bg"] = "#008B8B" 133 self.frm_status_label["text"] = "Close Serial Successful!" 134 self.frm_status_label["fg"] = "#8DEEEE" 135 136 def Open(self, event): 137 ''' 138 双击列表打开/关闭串口 139 ''' 140 self.Toggle() 141 142 def Clear(self): 143 self.frm_right_receive.delete("0.0", "end") 144 self.receive_count = 0 145 146 def Send(self): 147 ''' 148 向已打开的串口发送数据 149 如果为Hex发送,示例:"31 32 33" [即为字符串 "123"] 150 ''' 151 if self.ser: 152 try: 153 # 发送新行 154 if self.new_line_cbtn_var.get() == 0: 155 send_data = str(self.frm_right_send.get("0.0", "end").encode("gbk")).strip() 156 else: 157 send_data = str(self.frm_right_send.get("0.0", "end")).strip() + "\r\n" 158 159 # 是否十六进制发送 160 if self.send_hex_cbtn_var.get() == 1: 161 self.ser.write(send_data, isHex=True) 162 else: 163 self.ser.write(send_data) 164 except Exception as e: 165 self.frm_right_receive.insert("end", str(e) + "\n") 166 logging.error(e) 167 168 def SerialRead(self): 169 ''' 170 线程读取串口发送的数据 171 ''' 172 while self.ser.alive: 173 try: 174 n = self.ser.l_serial.inWaiting() 175 if n: 176 self.receive_data += self.ser.l_serial.read(n).replace(binascii.unhexlify("00"), "") 177 if self.thresholdValue <= len(self.receive_data): 178 self.receive_count += 1 179 180 # 接收显示是否为Hex 181 if self.receive_hex_cbtn_var.get() == 1: 182 self.receive_data = self.space_b2a_hex(self.receive_data) 183 self.frm_right_receive.insert("end", "[" + str(datetime.datetime.now()) + " - " 184 + str(self.receive_count) + "]:\n", "green") 185 self.frm_right_receive.insert("end", self.receive_data + "\n") 186 self.frm_right_receive.see("end") 187 self.receive_data = "" 188 189 except Exception as e: 190 logging.error(e) 191 self.receive_data = "" 192 self.ser.stop() 193 self.ser = None 194 195 def find_usb_tty(self, vendor_id=None, product_id=None): 196 ''' 197 发现串口设备 198 ''' 199 tty_devs = list() 200 for dn in glob.glob('/sys/bus/usb/devices/*') : 201 try: 202 vid = int(open(os.path.join(dn, "idVendor" )).read().strip(), 16) 203 pid = int(open(os.path.join(dn, "idProduct")).read().strip(), 16) 204 if ((vendor_id is None) or (vid == vendor_id)) and ((product_id is None) or (pid == product_id)) : 205 dns = glob.glob(os.path.join(dn, os.path.basename(dn) + "*")) 206 for sdn in dns : 207 for fn in glob.glob(os.path.join(sdn, "*")) : 208 if re.search(r"\/ttyUSB[0-9]+$", fn) : 209 tty_devs.append(os.path.join("/dev", os.path.basename(fn))) 210 except Exception as ex: 211 pass 212 return tty_devs 213 214 def space_b2a_hex(self, data): 215 ''' 216 格式化接收到的数据字符串 217 示例:123 --> 31 32 33 218 ''' 219 new_data_list = list() 220 new_data = "" 221 222 hex_data = binascii.b2a_hex(data) 223 temp_data = "" 224 for index,value in enumerate(hex_data): 225 temp_data += value 226 if len(temp_data) == 2: 227 new_data_list.append(temp_data) 228 temp_data = "" 229 for index,value in enumerate(new_data_list): 230 if index%25 == 0 and index != 0: 231 new_data += "\n" 232 new_data += value 233 new_data += " " 234 235 return new_data 236 237 if __name__ == '__main__': 238 ''' 239 main loop 240 ''' 241 root = tk.Tk() 242 root.title("Serial Tool") 243 if SerialTool.g_default_theme == "dark": 244 root.configure(bg="#292929") 245 combostyle = ttk.Style() 246 combostyle.theme_use('alt') 247 combostyle.configure("TCombobox", selectbackground="#292929", fieldbackground="#292929", 248 background="#292929", foreground="#FFFFFF") 249 MainSerialToolUI(master=root) 250 root.resizable(False, False) 251 root.mainloop()
项目地址:Serial-Tool
仅根据自己需要写的串口工具,需要其他功能的话请自行添加,或者告知我添加。