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.