host.nfc_tool.scan_tab
Scan tab — live tag presence indicator for range testing.
1"""Scan tab — live tag presence indicator for range testing.""" 2 3import tkinter as tk 4from tkinter import ttk 5 6 7class ScanTab(ttk.Frame): 8 """Large red/green indicator showing tag presence in real time.""" 9 10 def __init__(self, parent, request_poll=None): 11 super().__init__(parent) 12 self._request_poll = request_poll 13 self._polling = False 14 self._poll_id = None 15 16 self._build_ui() 17 18 def _build_ui(self): 19 # Large colored circle 20 self._canvas = tk.Canvas(self, width=200, height=200, 21 highlightthickness=0) 22 self._canvas.pack(pady=20) 23 self._indicator = self._canvas.create_oval( 24 25, 25, 175, 175, fill='#cc3333', outline='#882222', width=3, 25 ) 26 27 self._status_var = tk.StringVar(value="No tag detected") 28 ttk.Label( 29 self, textvariable=self._status_var, 30 font=('TkDefaultFont', 14), 31 ).pack(pady=(0, 10)) 32 33 # UID display 34 info_frame = ttk.Frame(self) 35 info_frame.pack(fill=tk.X, padx=40) 36 37 ttk.Label(info_frame, text="UID:").grid( 38 row=0, column=0, sticky=tk.W, padx=(0, 8)) 39 self._uid_var = tk.StringVar(value="—") 40 ttk.Label(info_frame, textvariable=self._uid_var, 41 font=('Consolas', 11)).grid( 42 row=0, column=1, sticky=tk.W) 43 44 ttk.Label(info_frame, text="Data size:").grid( 45 row=1, column=0, sticky=tk.W, padx=(0, 8), pady=(4, 0)) 46 self._size_var = tk.StringVar(value="—") 47 ttk.Label(info_frame, textvariable=self._size_var).grid( 48 row=1, column=1, sticky=tk.W, pady=(4, 0)) 49 50 # Auto-poll control 51 ctrl_frame = ttk.Frame(self) 52 ctrl_frame.pack(pady=20) 53 self._auto_var = tk.BooleanVar(value=True) 54 ttk.Checkbutton( 55 ctrl_frame, text="Auto-poll (200ms)", 56 variable=self._auto_var, 57 command=self._on_auto_toggle, 58 ).pack() 59 60 def on_tab_selected(self): 61 """Called when this tab becomes visible.""" 62 if self._auto_var.get(): 63 self._start_polling() 64 65 def on_tab_deselected(self): 66 """Called when switching away from this tab.""" 67 self._stop_polling() 68 69 def _on_auto_toggle(self): 70 if self._auto_var.get(): 71 self._start_polling() 72 else: 73 self._stop_polling() 74 75 def _start_polling(self): 76 if self._polling: 77 return 78 self._polling = True 79 self._do_poll() 80 81 def _stop_polling(self): 82 self._polling = False 83 if self._poll_id is not None: 84 self.after_cancel(self._poll_id) 85 self._poll_id = None 86 87 def _do_poll(self): 88 if not self._polling: 89 return 90 if self._request_poll: 91 self._request_poll('scan') 92 self._poll_id = self.after(200, self._do_poll) 93 94 def update_tag_data(self, tag_data): 95 """Called by app when worker returns scan result.""" 96 if tag_data is not None: 97 # Tag present 98 self._canvas.itemconfig(self._indicator, fill='#33cc33', 99 outline='#228822') 100 self._status_var.set("Tag detected!") 101 self._uid_var.set(tag_data.uid_hex) 102 data_len = len(tag_data.user_data) 103 if data_len > 0: 104 self._size_var.set(f"{data_len} bytes") 105 else: 106 self._size_var.set("UID only (data read failed)") 107 else: 108 # No tag 109 self._canvas.itemconfig(self._indicator, fill='#cc3333', 110 outline='#882222') 111 self._status_var.set("No tag detected") 112 self._uid_var.set("\u2014") 113 self._size_var.set("\u2014")
class
ScanTab(tkinter.ttk.Frame):
8class ScanTab(ttk.Frame): 9 """Large red/green indicator showing tag presence in real time.""" 10 11 def __init__(self, parent, request_poll=None): 12 super().__init__(parent) 13 self._request_poll = request_poll 14 self._polling = False 15 self._poll_id = None 16 17 self._build_ui() 18 19 def _build_ui(self): 20 # Large colored circle 21 self._canvas = tk.Canvas(self, width=200, height=200, 22 highlightthickness=0) 23 self._canvas.pack(pady=20) 24 self._indicator = self._canvas.create_oval( 25 25, 25, 175, 175, fill='#cc3333', outline='#882222', width=3, 26 ) 27 28 self._status_var = tk.StringVar(value="No tag detected") 29 ttk.Label( 30 self, textvariable=self._status_var, 31 font=('TkDefaultFont', 14), 32 ).pack(pady=(0, 10)) 33 34 # UID display 35 info_frame = ttk.Frame(self) 36 info_frame.pack(fill=tk.X, padx=40) 37 38 ttk.Label(info_frame, text="UID:").grid( 39 row=0, column=0, sticky=tk.W, padx=(0, 8)) 40 self._uid_var = tk.StringVar(value="—") 41 ttk.Label(info_frame, textvariable=self._uid_var, 42 font=('Consolas', 11)).grid( 43 row=0, column=1, sticky=tk.W) 44 45 ttk.Label(info_frame, text="Data size:").grid( 46 row=1, column=0, sticky=tk.W, padx=(0, 8), pady=(4, 0)) 47 self._size_var = tk.StringVar(value="—") 48 ttk.Label(info_frame, textvariable=self._size_var).grid( 49 row=1, column=1, sticky=tk.W, pady=(4, 0)) 50 51 # Auto-poll control 52 ctrl_frame = ttk.Frame(self) 53 ctrl_frame.pack(pady=20) 54 self._auto_var = tk.BooleanVar(value=True) 55 ttk.Checkbutton( 56 ctrl_frame, text="Auto-poll (200ms)", 57 variable=self._auto_var, 58 command=self._on_auto_toggle, 59 ).pack() 60 61 def on_tab_selected(self): 62 """Called when this tab becomes visible.""" 63 if self._auto_var.get(): 64 self._start_polling() 65 66 def on_tab_deselected(self): 67 """Called when switching away from this tab.""" 68 self._stop_polling() 69 70 def _on_auto_toggle(self): 71 if self._auto_var.get(): 72 self._start_polling() 73 else: 74 self._stop_polling() 75 76 def _start_polling(self): 77 if self._polling: 78 return 79 self._polling = True 80 self._do_poll() 81 82 def _stop_polling(self): 83 self._polling = False 84 if self._poll_id is not None: 85 self.after_cancel(self._poll_id) 86 self._poll_id = None 87 88 def _do_poll(self): 89 if not self._polling: 90 return 91 if self._request_poll: 92 self._request_poll('scan') 93 self._poll_id = self.after(200, self._do_poll) 94 95 def update_tag_data(self, tag_data): 96 """Called by app when worker returns scan result.""" 97 if tag_data is not None: 98 # Tag present 99 self._canvas.itemconfig(self._indicator, fill='#33cc33', 100 outline='#228822') 101 self._status_var.set("Tag detected!") 102 self._uid_var.set(tag_data.uid_hex) 103 data_len = len(tag_data.user_data) 104 if data_len > 0: 105 self._size_var.set(f"{data_len} bytes") 106 else: 107 self._size_var.set("UID only (data read failed)") 108 else: 109 # No tag 110 self._canvas.itemconfig(self._indicator, fill='#cc3333', 111 outline='#882222') 112 self._status_var.set("No tag detected") 113 self._uid_var.set("\u2014") 114 self._size_var.set("\u2014")
Large red/green indicator showing tag presence in real time.
ScanTab(parent, request_poll=None)
11 def __init__(self, parent, request_poll=None): 12 super().__init__(parent) 13 self._request_poll = request_poll 14 self._polling = False 15 self._poll_id = None 16 17 self._build_ui()
Construct a Ttk Frame with parent master.
STANDARD OPTIONS
class, cursor, style, takefocus
WIDGET-SPECIFIC OPTIONS
borderwidth, relief, padding, width, height
def
on_tab_selected(self):
61 def on_tab_selected(self): 62 """Called when this tab becomes visible.""" 63 if self._auto_var.get(): 64 self._start_polling()
Called when this tab becomes visible.
def
on_tab_deselected(self):
66 def on_tab_deselected(self): 67 """Called when switching away from this tab.""" 68 self._stop_polling()
Called when switching away from this tab.
def
update_tag_data(self, tag_data):
95 def update_tag_data(self, tag_data): 96 """Called by app when worker returns scan result.""" 97 if tag_data is not None: 98 # Tag present 99 self._canvas.itemconfig(self._indicator, fill='#33cc33', 100 outline='#228822') 101 self._status_var.set("Tag detected!") 102 self._uid_var.set(tag_data.uid_hex) 103 data_len = len(tag_data.user_data) 104 if data_len > 0: 105 self._size_var.set(f"{data_len} bytes") 106 else: 107 self._size_var.set("UID only (data read failed)") 108 else: 109 # No tag 110 self._canvas.itemconfig(self._indicator, fill='#cc3333', 111 outline='#882222') 112 self._status_var.set("No tag detected") 113 self._uid_var.set("\u2014") 114 self._size_var.set("\u2014")
Called by app when worker returns scan result.