# -*- coding: utf-8 -*- """Pruefstand: die eigene Menueleiste - Wechsel mit EINEM Klick, und am Rand. WARUM ES DIESE LEISTE GIBT -------------------------- `tk.Menu` ist unter Windows ein Menue des Betriebssystems. Es nimmt Farben entgegen und zeichnet trotzdem im Systemgrau - ein weisser Balken ueber einem dunklen Fenster. Sein Befund am 07.09.2026: *"nur die einstellunden hilfe leiste die ist noch hell"*. Deshalb ist die Leiste aus gewoehnlichen Labels gebaut. WAS HIER GEPRUEFT WIRD 1. Ein Klick auf einen Titel klappt seine Liste auf, ein zweiter auf denselben Titel klappt sie wieder zu. 2. Steht eine Liste offen und man klickt auf einen ANDEREN Titel, klappt dessen Liste auf - mit EINEM Klick. Das ging bis zum 07.09.2026 nicht. Solange eine Liste offen ist, haelt ihr `grab_set` alle Mausklicks der Anwendung fest; die ``-Bindung des Menuetitels feuert dann gar nicht. Der Klick landete beim Zuklappen und war damit verbraucht - man musste ein zweites Mal klicken, um von "Datei" zu "Hilfe" zu kommen. Das native tk.Menu wechselte schon beim blossen Ueberfahren; zwei Klicks waren also eine Verschlechterung gegenueber dem, was ersetzt wurde. Geprueft wird das ueber ein echtes Klickereignis auf der offenen Liste, mit den Bildschirmkoordinaten des anderen Titels - also genau der Weg, den der grab_set erzwingt. Ein direkter Aufruf von aufklappen() wuerde am Problem vorbeipruefen. 3. Die Liste bleibt auf dem Bildschirm. Steht das Fenster am unteren Rand, lief sie vorher darueber hinaus und war nicht mehr zu treffen: die Position wurde ungeprueft gesetzt, waehrend jede andere neue Position im Programm ueber theme.auf_bildschirm() laeuft. Diese Datei liegt in BEIDEN Programmen gleich. Wer sie aendert, kopiert sie hinueber - sonst steht in zwei Programmen zweierlei. """ import os import sys import time import tkinter as tk QUELLE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, QUELLE) import menueleiste # noqa: E402 import theme # noqa: E402 from pruefhelfer import fehler, pruefe # noqa: E402 class Fenster(theme.ThemeMixin, tk.Tk): """Ein nacktes Fenster mit Farben - mehr braucht die Leiste nicht.""" def __init__(self): super().__init__() self.cfg = {} # theme.modus() will etwas mit .get() self._apply_theme() self.title("Pruefstand Menueleiste") self.geometry("560x320" + theme.pruefplatz()) f = Fenster() gerufen = [] f.menue = menueleiste.Menueleiste(f, { "Datei": [("Neu", lambda: gerufen.append("neu")), ("-", None), ("Beenden", lambda: gerufen.append("beenden"))], "Bearbeiten": [("Rueckgaengig", lambda: gerufen.append("rueck"))], "Hilfe": [("Info", lambda: gerufen.append("info"))], }) def pumpen(sekunden=0.4): ende = time.time() + sekunden while time.time() < ende: f.update() time.sleep(0.02) pumpen(1.0) titel = {str(w.cget("text")): w for w in f.menue.winfo_children() if isinstance(w, tk.Label)} pruefe(set(titel) == {"Datei", "Bearbeiten", "Hilfe"}, f"alle drei Titel stehen in der Leiste ({sorted(titel)})") def klick_auf_titel(name): """Auf einen Menuetitel klicken - so, wie es die Maus tut. Steht schon eine Liste offen, MUSS das Ereignis an sie gehen: ihr grab_set faengt jeden Klick der Anwendung ab. Genau das ist der Fall, um den es unter Punkt 2 geht. """ w = titel[name] x = w.winfo_rootx() + w.winfo_width() // 2 y = w.winfo_rooty() + w.winfo_height() // 2 ziel = f.menue._offen or w if ziel is w: # Achtung: Tk nennt die Bildschirmkoordinaten beim Erzeugen -rootx # und -rooty; im Ereignis heissen sie dann x_root und y_root. w.event_generate("", x=w.winfo_width() // 2, y=w.winfo_height() // 2, rootx=x, rooty=y) else: ziel.event_generate("", x=-1, y=-1, rootx=x, rooty=y) pumpen(0.4) def offener_titel(): """Welcher Titel gerade offen ist - oder None.""" anker = f.menue._anker if anker is None or not f.menue._offen: return None return next((t for t, w in titel.items() if w is anker), "?") # ---- 1. Aufklappen und wieder zuklappen --------------------------------- klick_auf_titel("Datei") pruefe(offener_titel() == "Datei", f"'Datei' klappt auf ({offener_titel()})") klick_auf_titel("Datei") pruefe(offener_titel() is None, f"und derselbe Titel klappt wieder zu ({offener_titel()})") # ---- 2. Der Wechsel: EIN Klick ------------------------------------------ klick_auf_titel("Datei") pruefe(offener_titel() == "Datei", "'Datei' steht offen") klick_auf_titel("Hilfe") pruefe(offener_titel() == "Hilfe", f"ein Klick auf 'Hilfe' wechselt hinueber ({offener_titel()})") klick_auf_titel("Bearbeiten") pruefe(offener_titel() == "Bearbeiten", f"und weiter auf 'Bearbeiten' ({offener_titel()})") # Ein Klick irgendwo neben die Leiste schliesst - und oeffnet nichts. pop = f.menue._offen pop.event_generate("", x=-1, y=-1, rootx=f.winfo_rootx() + 300, rooty=f.winfo_rooty() + 250) pumpen(0.4) pruefe(offener_titel() is None, f"ein Klick daneben schliesst und oeffnet nichts ({offener_titel()})") # ---- 3. Ein Eintrag fuehrt seinen Befehl aus ---------------------------- klick_auf_titel("Hilfe") eintrag = None for kind in f.menue._offen.winfo_children(): for enkel in kind.winfo_children(): if isinstance(enkel, tk.Label) and str(enkel.cget("text")) == "Info": eintrag = enkel eintrag.event_generate("", x=2, y=2) pumpen(0.4) pruefe(gerufen == ["info"], f"der Eintrag 'Info' fuehrt seinen Befehl aus ({gerufen})") pruefe(offener_titel() is None, "und die Liste ist danach zu") # ---- 4. Am unteren Bildschirmrand bleibt die Liste sichtbar ------------- # Das Fenster so weit nach unten schieben, dass unter der Leiste kein Platz # mehr fuer die Klappliste ist. links, oben = theme.auf_bildschirm(f.winfo_rootx(), f.winfo_rooty()) unten = oben + f.winfo_screenheight() f.geometry(f"560x320+{links}+{unten - 120}") pumpen(0.6) klick_auf_titel("Datei") pop = f.menue._offen pruefe(pop is not None, "die Liste geht auch am unteren Rand auf") if pop is not None: x, y = pop.winfo_rootx(), pop.winfo_rooty() hoehe, breite = pop.winfo_height(), pop.winfo_width() grenze_x, grenze_y = theme.auf_bildschirm(x, y, breite, hoehe) pruefe((x, y) == (grenze_x, grenze_y), f"und steht vollstaendig auf dem Bildschirm " f"(Liste {x},{y} bis {x + breite},{y + hoehe})") f.destroy() if fehler: print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt") sys.exit(1) print("GRUEN | Die Menueleiste wechselt mit einem Klick und bleibt im Bild")