Popups kommen nach Klick woanders / Alt+Tab wieder nach vorn; Büroklammer im Tipp des Tages
Fehler: War ein Popup offen und man klickte in ein anderes Programm, ließ sich das Popup über Hauptfenster, Taskleiste oder Alt+Tab nicht mehr nach vorn holen. Ursache: Rahmenlose Tk-Fenster mit transient() legt Tk unter Windows als Kindfenster des Desktops (WS_CHILD) an – ohne Besitzer, daher kam beim Zurückholen nur das (gesperrte) Hauptfenster nach vorn. - Popups (Dialoge, Rückfragen, Rundgang) ohne transient(), stattdessen Windows-Besitzer + Sperre des aufrufenden Fensters wie bei echten modalen Windows-Dialogen (gui/win32.py) - Rundgang: neue Sprechblase erscheint vor dem Entfernen der alten (sonst springt Windows weg) - Alt+F4 und Modalsperre robust gegen Timing (Sperre wird bei Bedarf nachgeholt) - Tests (nur unter Windows/Wine): Besitzer, Sperre, Stapelreihenfolge nach „woanders klicken“ und Zurückholen, verschachtelte Rückfragen, Rundgang - Tipp des Tages mit freigestelltem Büroklammer-Bild (build/make_tipp_bild.py), in der .exe enthalten und vom Selbsttest geprüft Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aae28fa7f2
commit
26ac90bb0f
10 files changed
+417
-29
No files matched your search
+31
-5
@@ -508,9 +508,13 @@ class Dialog(tk.Toplevel):
|
||||
super().__init__(master, bg=FACE)
|
||||
self.withdraw()
|
||||
self.title(titel)
|
||||
self.transient(master.winfo_toplevel())
|
||||
if not KLASSISCH:
|
||||
# Rahmenlose Dialoge bekommen ihren Besitzer über win32.Modal – mit transient() würde Tk
|
||||
# sie unter Windows als Kindfenster anlegen, die dann hinter anderen Programmen hängen.
|
||||
self.transient(master.winfo_toplevel())
|
||||
self.resizable(resizable, resizable)
|
||||
self.result = None
|
||||
self._modal = None # Windows: Besitzer + Sperre des aufrufenden Fensters (siehe win32.Modal)
|
||||
self.protocol("WM_DELETE_WINDOW", self.abbrechen)
|
||||
self.bind("<Escape>", lambda e: self.abbrechen())
|
||||
try:
|
||||
@@ -537,11 +541,14 @@ class Dialog(tk.Toplevel):
|
||||
self.geometry(f"+{max(0, x)}+{max(0, y)}")
|
||||
self.deiconify()
|
||||
self.lift()
|
||||
# Windows: Dialog gehört dem aufrufenden Fenster, das solange gesperrt ist → Klick aufs
|
||||
# Hauptfenster, Taskleiste und Alt+Tab holen automatisch den Dialog nach vorn.
|
||||
from provtool.gui.win32 import Modal
|
||||
self.update_idletasks()
|
||||
self.focus_force()
|
||||
self._modal = Modal(self, m)
|
||||
vorher = self.grab_current() # z. B. der Assistent, wenn hier eine Rückfrage aufgeht
|
||||
try:
|
||||
self.grab_set()
|
||||
except tk.TclError:
|
||||
pass
|
||||
self._sperre_setzen()
|
||||
bind_id = None
|
||||
if KLASSISCH:
|
||||
# Ein rahmenloser Dialog darf nicht hinter dem Hauptfenster verschwinden
|
||||
@@ -563,6 +570,25 @@ class Dialog(tk.Toplevel):
|
||||
pass
|
||||
return self.result
|
||||
|
||||
def _sperre_setzen(self, versuch: int = 0):
|
||||
"""Tk-Modalsperre setzen. Ist das Fenster noch nicht ganz sichtbar (langsamer PC, Wine),
|
||||
schlägt grab_set fehl – dann kurz danach erneut versuchen statt ohne Sperre weiterzumachen."""
|
||||
try:
|
||||
if self.winfo_exists():
|
||||
self.grab_set()
|
||||
except tk.TclError:
|
||||
if versuch < 50:
|
||||
self.after(20, lambda: self._sperre_setzen(versuch + 1))
|
||||
|
||||
def destroy(self):
|
||||
# Erst das aufrufende Fenster wieder freigeben, dann schließen – sonst aktiviert Windows
|
||||
# beim Schließen ein fremdes Programm statt unseres Fensters.
|
||||
modal = getattr(self, "_modal", None)
|
||||
if modal is not None:
|
||||
modal.beenden()
|
||||
self._modal = None
|
||||
super().destroy()
|
||||
|
||||
def abbrechen(self):
|
||||
self.result = None
|
||||
self.destroy()
|
||||
|
||||
Reference in new issue
Block a user