# -*- coding: utf-8 -*- """Eine Menueleiste, die sich einfaerben laesst. WARUM NICHT DAS MENUE VON TK ---------------------------- `tk.Menu` ist unter Windows kein Tk-Widget, sondern ein Menue des Betriebssystems. Es nimmt Farben zwar entgegen, zeichnet aber trotzdem im Systemgrau - ein weisser Balken ueber einem dunklen Fenster. Sein Befund am 07.09.2026 zum Steuerrechnungstool: *"nur die einstellunden hilfe leiste die ist noch hell"*. Diese Leiste ist deshalb aus gewoehnlichen Labels gebaut: eine Zeile mit den Titeln, und beim Klick geht ein randloses Toplevel als Klappliste auf. Beides traegt die Farben des Programms. WAS SIE KANN UND WAS NICHT -------------------------- Sie kann, was diese beiden Programme brauchen: Titel, Eintraege, Trennlinien. Sie kann bewusst NICHT: Untermenues, Tastenkuerzel neben dem Eintrag, Ankreuzfelder. Wer das braucht, baut es hier ein - nicht daneben. BENUTZUNG self.menue = menueleiste.Menueleiste(self, farben=self, menues={ "Datei": [("Beenden", self._beenden)], "Hilfe": [("Info", self._info)], }) `farben` ist das Fenster mit den col_*-Feldern (das ThemeMixin). Ein "-" als Beschriftung ergibt eine Trennlinie. Diese Datei liegt in BEIDEN Programmen gleich. Wer sie aendert, kopiert sie hinueber. """ import tkinter as tk from tkinter import ttk class Menueleiste(tk.Frame): def __init__(self, master, menues, farben=None, mit_trennlinie=True): self.f = farben if farben is not None else master super().__init__(master, bg=self.f.col_bg) self.menues = menues self._offen = None self.pack(side="top", fill="x") for titel, eintraege in menues.items(): lbl = tk.Label(self, text=titel, bg=self.f.col_bg, fg=self.f.col_fg, padx=8, pady=3) lbl.pack(side="left") lbl.bind("", lambda e, w=lbl: w.configure(bg=self.f.col_sel)) lbl.bind("", lambda e, w=lbl: w.configure(bg=self.f.col_bg)) lbl.bind("", lambda e, b=lbl, it=eintraege: self.aufklappen(b, it)) if mit_trennlinie: ttk.Separator(master, orient="horizontal").pack(side="top", fill="x") # ---------------------------------------------------------------- Klappen def aufklappen(self, anker, eintraege): if self._offen: # zweiter Klick schliesst wieder self.zuklappen() return pop = tk.Toplevel(self) pop.overrideredirect(True) # Der Rahmen ist der Hintergrund des Toplevels, einen Pixel breit - # sonst zeichnet Windows einen weissen Rand um die Liste. pop.configure(bg=self.f.col_line) innen = tk.Frame(pop, bg=self.f.col_field) innen.pack(padx=1, pady=1) for beschriftung, befehl in eintraege: if beschriftung == "-": tk.Frame(innen, bg=self.f.col_line, height=1).pack(fill="x", pady=3) continue eintrag = tk.Label(innen, text=beschriftung, bg=self.f.col_field, fg=self.f.col_fg, anchor="w", padx=16, pady=5) eintrag.pack(fill="x") eintrag.bind("", lambda e, w=eintrag: w.configure(bg=self.f.col_sel)) eintrag.bind("", lambda e, w=eintrag: w.configure(bg=self.f.col_field)) eintrag.bind("", lambda e, c=befehl: self._ausfuehren(c)) pop.update_idletasks() pop.geometry(f"+{anker.winfo_rootx()}" f"+{anker.winfo_rooty() + anker.winfo_height()}") self._offen = pop pop.grab_set() pop.bind("", self._klick_ausserhalb, add="+") pop.bind("", lambda e: self.zuklappen()) faerben = getattr(self.f, "_dark_titlebar", None) if faerben: faerben(pop) def _klick_ausserhalb(self, ereignis): pop = self._offen if not pop: return if (ereignis.x_root < pop.winfo_rootx() or ereignis.x_root > pop.winfo_rootx() + pop.winfo_width() or ereignis.y_root < pop.winfo_rooty() or ereignis.y_root > pop.winfo_rooty() + pop.winfo_height()): self.zuklappen() def _ausfuehren(self, befehl): # Erst zuklappen, dann ausfuehren: sonst haelt der grab_set der Liste # einen Dialog fest, der aus dem Eintrag heraus aufgeht. self.zuklappen() if befehl: befehl() def zuklappen(self): if self._offen: try: self._offen.grab_release() self._offen.destroy() except tk.TclError: pass self._offen = None