fix(tray): replace tkinter messagebox with Win32 MessageBoxW
Lint & Test / test (push) Successful in 2m3s
Lint & Test / test (push) Successful in 2m3s
The packaged embedded Python distribution does not ship the tcl/tk runtime, so tkinter.messagebox.askyesno crashed with 'Can't find a usable init.tcl' when the user clicked Shutdown or Restart in the tray menu. Use ctypes + user32.MessageBoxW instead — no tcl/tk, no extra dependencies.
This commit is contained in:
@@ -4,9 +4,32 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tkinter import messagebox
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
|
def _confirm(message: str, title: str = "LED Grab") -> bool:
|
||||||
|
"""Show a Yes/No confirmation dialog using the Win32 API.
|
||||||
|
|
||||||
|
Uses ``ctypes`` instead of ``tkinter.messagebox`` because the packaged
|
||||||
|
embedded Python distribution does not include the tcl/tk runtime, which
|
||||||
|
causes ``tkinter`` to fail with ``Can't find a usable init.tcl``.
|
||||||
|
"""
|
||||||
|
if sys.platform != "win32":
|
||||||
|
# Non-Windows: no tray in practice, but fall back to auto-confirm.
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
# MB_YESNO = 0x04, MB_ICONQUESTION = 0x20, MB_SETFOREGROUND = 0x10000
|
||||||
|
# IDYES = 6
|
||||||
|
flags = 0x04 | 0x20 | 0x10000
|
||||||
|
result = ctypes.windll.user32.MessageBoxW(0, message, title, flags)
|
||||||
|
return result == 6
|
||||||
|
except Exception:
|
||||||
|
# If the dialog cannot be shown for any reason, proceed with the action.
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pystray
|
import pystray
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -60,9 +83,10 @@ class TrayManager:
|
|||||||
webbrowser.open(f"http://localhost:{self._port}")
|
webbrowser.open(f"http://localhost:{self._port}")
|
||||||
|
|
||||||
def _restart(self, icon: "pystray.Icon", item: "pystray.MenuItem") -> None:
|
def _restart(self, icon: "pystray.Icon", item: "pystray.MenuItem") -> None:
|
||||||
if not messagebox.askyesno("LED Grab", "Restart the server?"):
|
if not _confirm("Restart the server?"):
|
||||||
return
|
return
|
||||||
from wled_controller.server_ref import _broadcast_restarting
|
from wled_controller.server_ref import _broadcast_restarting
|
||||||
|
|
||||||
_broadcast_restarting()
|
_broadcast_restarting()
|
||||||
self._icon.stop()
|
self._icon.stop()
|
||||||
self._on_exit()
|
self._on_exit()
|
||||||
@@ -70,7 +94,7 @@ class TrayManager:
|
|||||||
os.execv(sys.executable, [sys.executable, "-m", "wled_controller"])
|
os.execv(sys.executable, [sys.executable, "-m", "wled_controller"])
|
||||||
|
|
||||||
def _shutdown(self, icon: "pystray.Icon", item: "pystray.MenuItem") -> None:
|
def _shutdown(self, icon: "pystray.Icon", item: "pystray.MenuItem") -> None:
|
||||||
if not messagebox.askyesno("LED Grab", "Shut down the server?"):
|
if not _confirm("Shut down the server?"):
|
||||||
return
|
return
|
||||||
self._on_exit()
|
self._on_exit()
|
||||||
self._icon.stop()
|
self._icon.stop()
|
||||||
|
|||||||
Reference in New Issue
Block a user