Initial public release
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
"""Modal popup invoked when a device sends a Request BLE Variable(s) op.
|
||||
|
||||
The dialog lists the variable names the device asked for, pre-fills with the
|
||||
current device-profile values (if any), and returns the user's edits via the
|
||||
on_submit callback. Clicking Cancel returns None — the BLE client falls back
|
||||
to the existing values in that case.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
|
||||
|
||||
class RequestVariableDialog(tk.Toplevel):
|
||||
_BG = "#2D2D3D"
|
||||
_ROW_BG = "#252535"
|
||||
_ENTRY_BG = "#1E1E2E"
|
||||
_FG = "#FFFFFF"
|
||||
_DIM_FG = "#AAAAAA"
|
||||
_BTN_BG = "#3D3D5C"
|
||||
_SAVE_BG = "#0077CC"
|
||||
|
||||
def __init__(self, parent, mac: str, names: list, current: dict,
|
||||
on_submit, play_sound: bool = True):
|
||||
super().__init__(parent)
|
||||
self.title(f"Variable Request — {mac}")
|
||||
self.geometry("520x420")
|
||||
self.minsize(380, 240)
|
||||
self.configure(bg=self._BG)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
|
||||
self._on_submit = on_submit
|
||||
self._mac = mac
|
||||
self._names = list(names)
|
||||
self._current = dict(current or {})
|
||||
self._rows = []
|
||||
|
||||
try:
|
||||
import ctypes
|
||||
self.update_idletasks()
|
||||
hwnd = ctypes.windll.user32.GetParent(self.winfo_id())
|
||||
ctypes.windll.dwmapi.DwmSetWindowAttribute(
|
||||
hwnd, 20, ctypes.byref(ctypes.c_int(1)), ctypes.sizeof(ctypes.c_int))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._build_ui()
|
||||
# Intentionally NOT binding <Return> globally on the Toplevel: a stray
|
||||
# Enter keystroke pending in the OS keyboard queue when this dialog
|
||||
# focus_force()s itself in would auto-submit before the user types,
|
||||
# send empty values back to the device, and "end the routine."
|
||||
# Submission goes through the Send to Device button only.
|
||||
self.protocol("WM_DELETE_WINDOW", self._cancel)
|
||||
|
||||
if play_sound:
|
||||
try:
|
||||
import winsound
|
||||
winsound.MessageBeep(winsound.MB_ICONEXCLAMATION)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.lift()
|
||||
self.focus_force()
|
||||
|
||||
def _build_ui(self):
|
||||
hdr = tk.Frame(self, bg=self._BG)
|
||||
hdr.pack(fill="x", padx=14, pady=(12, 4))
|
||||
tk.Label(hdr, text="Variable Request", bg=self._BG, fg="#0AACFF",
|
||||
font=("Segoe UI", 11, "bold")).pack(side="left")
|
||||
tk.Label(hdr, text=f"Device {self._mac}", bg=self._BG, fg=self._DIM_FG,
|
||||
font=("Segoe UI", 8)).pack(side="left", padx=(10, 0))
|
||||
|
||||
tk.Label(self, bg=self._BG, fg=self._DIM_FG, font=("Segoe UI", 8),
|
||||
text="Fill in values below; the device will receive them and "
|
||||
"the device profile will be updated.").pack(
|
||||
anchor="w", padx=14)
|
||||
|
||||
outer = tk.Frame(self, bg=self._BG)
|
||||
outer.pack(fill="both", expand=True, padx=14, pady=8)
|
||||
|
||||
canvas = tk.Canvas(outer, bg=self._BG, highlightthickness=0)
|
||||
scrollbar = tk.Scrollbar(outer, orient="vertical", command=canvas.yview)
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
canvas.pack(side="left", fill="both", expand=True)
|
||||
|
||||
rows_frame = tk.Frame(canvas, bg=self._BG)
|
||||
rows_window = canvas.create_window((0, 0), window=rows_frame, anchor="nw")
|
||||
rows_frame.bind("<Configure>",
|
||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
|
||||
canvas.bind("<Configure>",
|
||||
lambda e: canvas.itemconfig(rows_window, width=e.width))
|
||||
|
||||
first_entry = None
|
||||
for name in self._names:
|
||||
row = tk.Frame(rows_frame, bg=self._ROW_BG, pady=3)
|
||||
row.pack(fill="x", pady=2)
|
||||
tk.Label(row, text=name, bg=self._ROW_BG, fg=self._FG,
|
||||
font=("Segoe UI", 9, "bold"), width=18, anchor="w").pack(
|
||||
side="left", padx=(8, 4))
|
||||
var = tk.StringVar(value=str(self._current.get(name, "")))
|
||||
entry = tk.Entry(row, textvariable=var, bg=self._ENTRY_BG,
|
||||
fg=self._FG, insertbackground=self._FG,
|
||||
font=("Segoe UI", 9), relief="flat")
|
||||
entry.pack(side="left", fill="x", expand=True, padx=(0, 8), ipady=3)
|
||||
self._rows.append((name, var))
|
||||
if first_entry is None:
|
||||
first_entry = entry
|
||||
|
||||
if first_entry is not None:
|
||||
first_entry.focus_set()
|
||||
|
||||
btns = tk.Frame(self, bg=self._BG)
|
||||
btns.pack(fill="x", padx=14, pady=(4, 12))
|
||||
tk.Button(btns, text="Cancel", bg=self._BTN_BG, fg=self._FG,
|
||||
relief="flat", padx=12, font=("Segoe UI", 9),
|
||||
command=self._cancel).pack(side="right", padx=(6, 0))
|
||||
tk.Button(btns, text="Send to Device", bg=self._SAVE_BG, fg=self._FG,
|
||||
relief="flat", padx=14, font=("Segoe UI", 9, "bold"),
|
||||
command=self._submit).pack(side="right")
|
||||
|
||||
def _submit(self):
|
||||
result = {name: var.get() for name, var in self._rows}
|
||||
try:
|
||||
self._on_submit(result)
|
||||
except Exception as e:
|
||||
messagebox.showerror("Variable Request", str(e), parent=self)
|
||||
return
|
||||
self.destroy()
|
||||
|
||||
def _cancel(self):
|
||||
try:
|
||||
self._on_submit(None)
|
||||
except Exception:
|
||||
pass
|
||||
self.destroy()
|
||||
Reference in New Issue
Block a user