Sein Befund "und beim steuertool fehlt das theam in genze oder wird nicht durch die automatik gesetzt" "nur die einstellunden hilfe leiste die ist noch hell und der icon und titel leiste auch" Er hatte recht, und zwar wortwoertlich: dieses Programm lief im Tk-Standard, mit dreimal fest eingetragenem foreground="black". Es gab hier gar keine theme.py. Gebaut theme.py aus dem Rechnungstool uebernommen und dabei von config.py geloest - eine Datei, die in BEIDEN Programmen liegt, darf nichts aus einem der beiden importieren. Beim ersten Uebernehmen flog sie genau daran beim Import auseinander. Die Einstellungen liegen hier in der Journal-Tabelle, nicht in einer config.json. _einstellungen() ist die Bruecke: etwas mit .get() und []=, wie hinweis.py und theme.py es erwarten. Menueleiste aus menueleiste.py statt tk.Menu - ein natives Menue laesst sich unter Windows nicht einfaerben, das war der letzte helle Rest. Treeview-Stil in theme.py ergaenzt (ttk zeichnet Listen nicht ueber den Grundstil), Reiter schmaler, damit alle zwoelf Monatsnamen lesbar bleiben. Die Summenleisten in gui_monat.py holen ihre Farben jetzt vom Fenster statt sie fest zu tragen - sie waren ein weisser Block unten im dunklen Bild. Logo und Fenstersymbol werden fuer die dunkle Darstellung aufgehellt. ⚠️ Altfehler behoben: die Meldung des Rechnungstools stand nie pruef_wache lief mal gruen und mal rot - je nachdem, wann er hinsah. Das war kein Wackler im Pruefstand, sondern sein Symptom: meldet das Rechnungstool eine neue Rechnung, laeuft sofort das Einlesen an, und dessen Ergebnis landete in derselben Zeile. Der Benutzer sah NIE, dass etwas hereingekommen war. melde() kennt jetzt einen Vorrang in Sekunden; die Meldung des Rechnungstools bekommt sechs. Dreimal hintereinander stabil gruen. Ein wackelnder Pruefstand ist keine Nebensache, die man wegdrueckt. AUFGABE_STEUERSAETZE_DYNAMISCH.md Seine naechste Ansage notiert: die festen Saetze sollen raus, dynamisch per Tabelle wie im Rechnungstool. Mit dem, worauf dabei zu achten ist - ein Satz gilt ab einem DATUM, und die Saetze bereits erfasster Buchungen bleiben stehen (§ 147 AO). Fassung 1.9.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
120 lines
4.7 KiB
Python
120 lines
4.7 KiB
Python
# -*- 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("<Enter>", lambda e, w=lbl: w.configure(bg=self.f.col_sel))
|
|
lbl.bind("<Leave>", lambda e, w=lbl: w.configure(bg=self.f.col_bg))
|
|
lbl.bind("<Button-1>",
|
|
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("<Enter>",
|
|
lambda e, w=eintrag: w.configure(bg=self.f.col_sel))
|
|
eintrag.bind("<Leave>",
|
|
lambda e, w=eintrag: w.configure(bg=self.f.col_field))
|
|
eintrag.bind("<Button-1>", 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("<Button-1>", self._klick_ausserhalb, add="+")
|
|
pop.bind("<Escape>", 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
|