host.nfc_tool.serial_panel
Serial port connection panel — shared across all tabs.
1"""Serial port connection panel — shared across all tabs.""" 2 3import tkinter as tk 4from tkinter import ttk 5 6import serial.tools.list_ports 7 8 9class SerialPanel(ttk.Frame): 10 """COM port selector with connect/disconnect toggle.""" 11 12 def __init__(self, parent, on_connect=None, on_disconnect=None): 13 super().__init__(parent) 14 self._on_connect = on_connect 15 self._on_disconnect = on_disconnect 16 self._connected = False 17 18 self._build_ui() 19 self._refresh_ports() 20 21 def _build_ui(self): 22 ttk.Label(self, text="Port:").pack(side=tk.LEFT, padx=(0, 4)) 23 24 self._port_var = tk.StringVar() 25 self._port_combo = ttk.Combobox( 26 self, textvariable=self._port_var, 27 state='readonly', width=30, 28 ) 29 self._port_combo.pack(side=tk.LEFT, padx=(0, 4)) 30 31 self._refresh_btn = ttk.Button( 32 self, text="Refresh", command=self._refresh_ports, width=8, 33 ) 34 self._refresh_btn.pack(side=tk.LEFT, padx=(0, 8)) 35 36 self._connect_btn = ttk.Button( 37 self, text="Connect", command=self._toggle_connect, width=12, 38 ) 39 self._connect_btn.pack(side=tk.LEFT, padx=(0, 8)) 40 41 self._status_var = tk.StringVar(value="Disconnected") 42 self._status_label = ttk.Label( 43 self, textvariable=self._status_var, 44 foreground='gray', 45 ) 46 self._status_label.pack(side=tk.LEFT) 47 48 def _refresh_ports(self): 49 """Rescan available serial ports.""" 50 ports = serial.tools.list_ports.comports() 51 descriptions = [] 52 self._port_map = {} 53 for p in sorted(ports, key=lambda x: x.device): 54 desc = f"{p.device} - {p.description}" 55 descriptions.append(desc) 56 self._port_map[desc] = p.device 57 58 self._port_combo['values'] = descriptions 59 if descriptions and not self._port_var.get(): 60 self._port_combo.current(0) 61 62 def _toggle_connect(self): 63 if self._connected: 64 self._disconnect() 65 else: 66 self._connect() 67 68 def _connect(self): 69 desc = self._port_var.get() 70 port = self._port_map.get(desc, desc) 71 if not port: 72 self.set_status("No port selected", 'red') 73 return 74 75 self.set_status("Connecting...", 'orange') 76 self._set_controls_enabled(False) 77 78 if self._on_connect: 79 self._on_connect(port) 80 81 def _disconnect(self): 82 if self._on_disconnect: 83 self._on_disconnect() 84 85 def set_connected(self, success, error_msg=''): 86 """Called by app after worker reports connect result.""" 87 if success: 88 self._connected = True 89 self._connect_btn.configure(text="Disconnect") 90 self.set_status("Connected", 'green') 91 self._port_combo.configure(state='disabled') 92 self._refresh_btn.configure(state='disabled') 93 else: 94 self._connected = False 95 self._set_controls_enabled(True) 96 msg = f"Error: {error_msg}" if error_msg else "Connection failed" 97 self.set_status(msg, 'red') 98 99 def set_disconnected(self): 100 """Called by app after disconnect.""" 101 self._connected = False 102 self._connect_btn.configure(text="Connect") 103 self._set_controls_enabled(True) 104 self.set_status("Disconnected", 'gray') 105 106 def set_status(self, text, color='gray'): 107 self._status_var.set(text) 108 self._status_label.configure(foreground=color) 109 110 def _set_controls_enabled(self, enabled): 111 state = 'readonly' if enabled else 'disabled' 112 self._port_combo.configure(state=state) 113 self._refresh_btn.configure( 114 state='normal' if enabled else 'disabled') 115 self._connect_btn.configure(text="Connect") 116 117 @property 118 def is_connected(self): 119 return self._connected
class
SerialPanel(tkinter.ttk.Frame):
10class SerialPanel(ttk.Frame): 11 """COM port selector with connect/disconnect toggle.""" 12 13 def __init__(self, parent, on_connect=None, on_disconnect=None): 14 super().__init__(parent) 15 self._on_connect = on_connect 16 self._on_disconnect = on_disconnect 17 self._connected = False 18 19 self._build_ui() 20 self._refresh_ports() 21 22 def _build_ui(self): 23 ttk.Label(self, text="Port:").pack(side=tk.LEFT, padx=(0, 4)) 24 25 self._port_var = tk.StringVar() 26 self._port_combo = ttk.Combobox( 27 self, textvariable=self._port_var, 28 state='readonly', width=30, 29 ) 30 self._port_combo.pack(side=tk.LEFT, padx=(0, 4)) 31 32 self._refresh_btn = ttk.Button( 33 self, text="Refresh", command=self._refresh_ports, width=8, 34 ) 35 self._refresh_btn.pack(side=tk.LEFT, padx=(0, 8)) 36 37 self._connect_btn = ttk.Button( 38 self, text="Connect", command=self._toggle_connect, width=12, 39 ) 40 self._connect_btn.pack(side=tk.LEFT, padx=(0, 8)) 41 42 self._status_var = tk.StringVar(value="Disconnected") 43 self._status_label = ttk.Label( 44 self, textvariable=self._status_var, 45 foreground='gray', 46 ) 47 self._status_label.pack(side=tk.LEFT) 48 49 def _refresh_ports(self): 50 """Rescan available serial ports.""" 51 ports = serial.tools.list_ports.comports() 52 descriptions = [] 53 self._port_map = {} 54 for p in sorted(ports, key=lambda x: x.device): 55 desc = f"{p.device} - {p.description}" 56 descriptions.append(desc) 57 self._port_map[desc] = p.device 58 59 self._port_combo['values'] = descriptions 60 if descriptions and not self._port_var.get(): 61 self._port_combo.current(0) 62 63 def _toggle_connect(self): 64 if self._connected: 65 self._disconnect() 66 else: 67 self._connect() 68 69 def _connect(self): 70 desc = self._port_var.get() 71 port = self._port_map.get(desc, desc) 72 if not port: 73 self.set_status("No port selected", 'red') 74 return 75 76 self.set_status("Connecting...", 'orange') 77 self._set_controls_enabled(False) 78 79 if self._on_connect: 80 self._on_connect(port) 81 82 def _disconnect(self): 83 if self._on_disconnect: 84 self._on_disconnect() 85 86 def set_connected(self, success, error_msg=''): 87 """Called by app after worker reports connect result.""" 88 if success: 89 self._connected = True 90 self._connect_btn.configure(text="Disconnect") 91 self.set_status("Connected", 'green') 92 self._port_combo.configure(state='disabled') 93 self._refresh_btn.configure(state='disabled') 94 else: 95 self._connected = False 96 self._set_controls_enabled(True) 97 msg = f"Error: {error_msg}" if error_msg else "Connection failed" 98 self.set_status(msg, 'red') 99 100 def set_disconnected(self): 101 """Called by app after disconnect.""" 102 self._connected = False 103 self._connect_btn.configure(text="Connect") 104 self._set_controls_enabled(True) 105 self.set_status("Disconnected", 'gray') 106 107 def set_status(self, text, color='gray'): 108 self._status_var.set(text) 109 self._status_label.configure(foreground=color) 110 111 def _set_controls_enabled(self, enabled): 112 state = 'readonly' if enabled else 'disabled' 113 self._port_combo.configure(state=state) 114 self._refresh_btn.configure( 115 state='normal' if enabled else 'disabled') 116 self._connect_btn.configure(text="Connect") 117 118 @property 119 def is_connected(self): 120 return self._connected
COM port selector with connect/disconnect toggle.
SerialPanel(parent, on_connect=None, on_disconnect=None)
13 def __init__(self, parent, on_connect=None, on_disconnect=None): 14 super().__init__(parent) 15 self._on_connect = on_connect 16 self._on_disconnect = on_disconnect 17 self._connected = False 18 19 self._build_ui() 20 self._refresh_ports()
Construct a Ttk Frame with parent master.
STANDARD OPTIONS
class, cursor, style, takefocus
WIDGET-SPECIFIC OPTIONS
borderwidth, relief, padding, width, height
def
set_connected(self, success, error_msg=''):
86 def set_connected(self, success, error_msg=''): 87 """Called by app after worker reports connect result.""" 88 if success: 89 self._connected = True 90 self._connect_btn.configure(text="Disconnect") 91 self.set_status("Connected", 'green') 92 self._port_combo.configure(state='disabled') 93 self._refresh_btn.configure(state='disabled') 94 else: 95 self._connected = False 96 self._set_controls_enabled(True) 97 msg = f"Error: {error_msg}" if error_msg else "Connection failed" 98 self.set_status(msg, 'red')
Called by app after worker reports connect result.
def
set_disconnected(self):
100 def set_disconnected(self): 101 """Called by app after disconnect.""" 102 self._connected = False 103 self._connect_btn.configure(text="Connect") 104 self._set_controls_enabled(True) 105 self.set_status("Disconnected", 'gray')
Called by app after disconnect.