"""Modale Sperre ohne Tk-„grab“: Nur der INHALT des aufrufenden Fensters wird gesperrt. Titelleiste und Rand bleiben bedienbar – das Hauptfenster lässt sich also verschieben, maximieren und in der Größe ändern, während ein Dialog offen ist. Klickt man auf den gesperrten Inhalt, kommt der Dialog nach vorn und blinkt kurz (wie bei Windows). Technik: Jedes Widget im gesperrten Bereich bekommt vorn das Bindungs-Tag ``Pm95Gesperrt``. Dessen Bindungen laufen vor allen anderen und beenden Klicks/Tastendrücke mit „break“. """ from __future__ import annotations import tkinter as tk TAG = "Pm95Gesperrt" _stapel: list[tk.Toplevel] = [] # offene modale Fenster, innerstes zuletzt GESPERRTE_EREIGNISSE = ("", "", "", "", "", "", "", "") def oberstes() -> tk.Toplevel | None: """Das zuletzt geöffnete, noch offene modale Fenster (oder None).""" while _stapel: d = _stapel[-1] try: if d.winfo_exists(): return d except tk.TclError: pass _stapel.pop() return None def offen() -> bool: return oberstes() is not None def _gesperrt_geklickt(event): d = oberstes() if d is not None and hasattr(d, "nach_vorn"): d.nach_vorn(blinken=str(event.type) in ("ButtonPress", "4")) return "break" def _einrichten(root: tk.Misc): # Merker am Tk-Objekt selbst (nicht am Namen: das Hauptfenster heißt in jeder Tk-Instanz „.“) if getattr(root, "_pm95_sperre_eingerichtet", False): return for seq in GESPERRTE_EREIGNISSE: root.bind_class(TAG, seq, _gesperrt_geklickt) root._pm95_sperre_eingerichtet = True def sperrbereiche(fenster: tk.Misc) -> list[tk.Misc]: """Welche Widgets eines Fensters gesperrt werden: alles außer Titelleiste (und Rand).""" eigene = getattr(fenster, "sperrbereich", None) if callable(eigene): return list(eigene()) titel = getattr(fenster, "titelleiste", None) return [k for k in fenster.winfo_children() if k is not titel and not isinstance(k, tk.Toplevel)] class Sperre: """Sperrt den Inhalt von `fenster`, solange `dialog` offen ist.""" def __init__(self, fenster: tk.Misc, dialog: tk.Toplevel): _einrichten(fenster._root()) self.widgets: list[tk.Misc] = [] for bereich in sperrbereiche(fenster): self._sperren(bereich) _stapel.append(dialog) self.dialog = dialog def _sperren(self, w: tk.Misc): if isinstance(w, tk.Toplevel): return # eigene Fenster (z. B. andere Dialoge) nicht anfassen tags = w.bindtags() if TAG not in tags: w.bindtags((TAG,) + tuple(tags)) self.widgets.append(w) for kind in w.winfo_children(): self._sperren(kind) def aufheben(self): for w in self.widgets: try: if w.winfo_exists(): w.bindtags(tuple(t for t in w.bindtags() if t != TAG)) except tk.TclError: pass self.widgets = [] if self.dialog in _stapel: _stapel.remove(self.dialog)