需要与串口设备进行通讯,那么一个调试工具是必须的。

根据我自己的需要,写了个简易版本的串口调试工具:

预览图:

串口调试工具(Python2.7+pyserial+Tkinter)-冯金伟博客园

======================

项目结构:

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("
", "")
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()

View Code

======================

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)

View Code

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()

View Code

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()

View Code

======================

界面逻辑主程序

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() + "
"  
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) + "
")
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) + "]:
", "green")
185                         self.frm_right_receive.insert("end", self.receive_data + "
")
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 += "
"
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()

View Code

项目地址:Serial-Tool

仅根据自己需要写的串口工具,需要其他功能的话请自行添加,或者告知我添加。