Reiter selbst gebaut - clam liess den ruhenden nicht dunkel werden

Sein Fund, zweimal: "er hebt den aktiven tab nicht hervor, der inaktive ist der
hervorgehobene". Gemessen statt geraten: der clam-Standard faerbt die
Reiterflaeche #bab5ab, und weder background noch lightcolor/darkcolor haben das
im laufenden Fenster zuverlaessig ueberschrieben.

Statt weiter daran zu drehen ist die Leiste jetzt selbst gebaut (reiter.py) -
und sieht dabei aus wie die Reiter in seinen Web-Oberflaechen: flacher Knopf,
der offene in Akzentfarbe, fett, mit Balken darunter und der Flaeche der Kachel.

Nach aussen verhaelt sie sich wie ein Notebook: add, select, index("current"/
"end"), tab(i, "text") und <<NotebookTabChanged>>. Deshalb bleiben der
Storno-Weg (korrektur.py waehlt select(1)) und pruef_montage unveraendert -
alle 14 Pruefstaende gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
This commit is contained in:
TheMockTv 2026-09-05 19:21:20 +02:00
parent 2e3a191f19
commit 1c2b115108
3 changed files with 112 additions and 6 deletions

12
app.py
View file

@ -39,6 +39,7 @@ from bestand import (hoechster_zaehler_im_ordner, naechste_freie_nummer,
vorhandene_rechnungen) vorhandene_rechnungen)
from einzelinstanz import (eigene_pid_merken, einzelinstanz_pruefen, fenster_nach_vorn, from einzelinstanz import (eigene_pid_merken, einzelinstanz_pruefen, fenster_nach_vorn,
laufende_pid_lesen) laufende_pid_lesen)
import reiter
import theme import theme
from theme import MeldungMixin, ThemeMixin from theme import MeldungMixin, ThemeMixin
from korrektur import KorrekturMixin from korrektur import KorrekturMixin
@ -374,13 +375,16 @@ class RechnungsApp(ThemeMixin, MeldungMixin, KorrekturMixin, EinstellungenMixin,
# Leistungskatalog und die Montage-Rechnung mit der Monatsmiete. Der # Leistungskatalog und die Montage-Rechnung mit der Monatsmiete. Der
# Kopf (Kunde, Nummer, Datum, Zeitraum) und die Summenleiste gelten fuer # Kopf (Kunde, Nummer, Datum, Zeitraum) und die Summenleiste gelten fuer
# beide - nur die Mitte wechselt. So tippt niemand den Kunden zweimal. # beide - nur die Mitte wechselt. So tippt niemand den Kunden zweimal.
self.blaetter = ttk.Notebook(self) # Eigene Reiterleiste statt ttk.Notebook: unter clam liess sich der
self.blaetter.pack(side="top", fill="both", expand=True, padx=12, pady=4) # ruhende Reiter nicht dunkel bekommen, er sah aus wie der offene.
# Nach aussen verhaelt sie sich wie ein Notebook (siehe reiter.py).
self.blaetter = reiter.Reiterleiste(self, farben=self)
self.blaetter.pack(side="top", fill="both", expand=True, padx=16, pady=(4, 6))
self.blaetter.bind("<<NotebookTabChanged>>", lambda _e: self._blatt_gewechselt()) self.blaetter.bind("<<NotebookTabChanged>>", lambda _e: self._blatt_gewechselt())
camping = ttk.Frame(self.blaetter, padding=(2, 4)) camping = ttk.Frame(self.blaetter.bereich, padding=(2, 6))
self.blaetter.add(camping, text=" Campingrechnung ") self.blaetter.add(camping, text=" Campingrechnung ")
montage_tab = ttk.Frame(self.blaetter, padding=(10, 10)) montage_tab = ttk.Frame(self.blaetter.bereich, padding=(14, 14))
self.blaetter.add(montage_tab, text=" Montage ") self.blaetter.add(montage_tab, text=" Montage ")
self._build_montage(montage_tab) self._build_montage(montage_tab)

94
reiter.py Normal file
View file

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
"""Reiterleiste im Pcore-Stil: Knopf mit Unterstrich statt ttk-Karteikarte.
WARUM NICHT ttk.Notebook
------------------------
Unter dem clam-Thema laesst sich die Reiterflaeche nicht zuverlaessig
einfaerben: der RUHENDE Reiter blieb hell und sah aus wie der ausgewaehlte -
also genau verkehrt herum. Sein Fund: *"er hebt den aktiven tab nicht hervor,
der inaktive ist der hervorgehobene"*.
Statt weiter an clam zu drehen, ist die Leiste hier selbst gebaut - und sieht
dabei aus wie die Reiter in seinen Web-Oberflaechen: ein flacher Knopf, der
offene in Akzentfarbe mit einem Balken darunter.
NACH AUSSEN wie ein Notebook
----------------------------
`add`, `select`, `index("current"/"end")`, `tab(i, "text")` und das Ereignis
`<<NotebookTabChanged>>` verhalten sich wie beim Original. So bleiben die
Aufrufer unveraendert - der Storno-Weg waehlt weiter `select(1)`, und die
Pruefstaende lesen die Reiternamen wie bisher.
"""
import tkinter as tk
from tkinter import ttk
import theme
class Reiterleiste(ttk.Frame):
def __init__(self, master, farben=None):
super().__init__(master)
self.f = farben if farben is not None else master
self._seiten = [] # (Rahmen, Text, Knopf, Balken)
self._aktiv = 0
self.leiste = ttk.Frame(self)
self.leiste.pack(side="top", fill="x")
self.bereich = ttk.Frame(self)
self.bereich.pack(side="top", fill="both", expand=True)
# ---- wie beim Notebook ------------------------------------------------
def add(self, rahmen, text=""):
nr = len(self._seiten)
kasten = ttk.Frame(self.leiste)
kasten.pack(side="left", padx=(0, 4))
knopf = tk.Label(kasten, text=text.strip(), cursor="hand2",
bg=self.f.col_bg, fg=self.f.col_muted,
font=(theme.SCHRIFT_NAME, 10), padx=18, pady=8)
knopf.pack(side="top", fill="x")
# Der Balken unter dem offenen Reiter - dasselbe Zeichen wie in den
# Web-Oberflaechen.
balken = tk.Frame(kasten, height=2, bg=self.f.col_bg)
balken.pack(side="top", fill="x")
knopf.bind("<Button-1>", lambda _e, i=nr: self.select(i))
knopf.bind("<Enter>", lambda _e, i=nr: self._schweben(i, True))
knopf.bind("<Leave>", lambda _e, i=nr: self._schweben(i, False))
self._seiten.append([rahmen, text, knopf, balken])
if nr == 0:
rahmen.pack(in_=self.bereich, fill="both", expand=True)
self._zeichnen()
def select(self, nr):
nr = int(nr)
if not (0 <= nr < len(self._seiten)) or nr == self._aktiv:
self._zeichnen()
return
self._seiten[self._aktiv][0].pack_forget()
self._aktiv = nr
self._seiten[nr][0].pack(in_=self.bereich, fill="both", expand=True)
self._zeichnen()
# Dasselbe Ereignis wie beim Notebook - die App haengt daran.
self.event_generate("<<NotebookTabChanged>>")
def index(self, was="current"):
return len(self._seiten) if was == "end" else self._aktiv
def tab(self, nr, key="text"):
return self._seiten[int(nr)][1] if key == "text" else ""
# ---- Aussehen ---------------------------------------------------------
def _schweben(self, nr, drueber):
if nr == self._aktiv:
return
knopf = self._seiten[nr][2]
knopf.configure(fg=self.f.col_fg if drueber else self.f.col_muted)
def _zeichnen(self):
for i, (_rahmen, _text, knopf, balken) in enumerate(self._seiten):
offen = (i == self._aktiv)
knopf.configure(
fg=self.f.col_accent if offen else self.f.col_muted,
bg=self.f.col_card if offen else self.f.col_bg,
font=(theme.SCHRIFT_NAME, 10, "bold") if offen
else (theme.SCHRIFT_NAME, 10))
balken.configure(bg=self.f.col_accent if offen else self.f.col_bg)

View file

@ -268,12 +268,20 @@ class ThemeMixin:
# Der OFFENE Reiter gehoert zur Kachel darunter und ist deshalb heller # Der OFFENE Reiter gehoert zur Kachel darunter und ist deshalb heller
# als die ruhenden. Vorher war es andersherum: die ruhenden lagen auf der # als die ruhenden. Vorher war es andersherum: die ruhenden lagen auf der
# helleren Flaeche und sahen aus, als waeren sie ausgewaehlt. # helleren Flaeche und sahen aus, als waeren sie ausgewaehlt.
style.configure("TNotebook.Tab", background=bg, # ⚠️ clam zeichnet die Reiterflaeche aus lightcolor/darkcolor, nicht aus
# background allein - werden die nicht mitgesetzt, bleibt der ruhende
# Reiter hell und sieht aus wie der ausgewaehlte.
style.configure("TNotebook.Tab", background=bg, lightcolor=bg, darkcolor=bg,
foreground=self.col_muted, bordercolor=self.col_line, foreground=self.col_muted, bordercolor=self.col_line,
padding=(20, 9), font=(SCHRIFT_NAME, 10), borderwidth=0) padding=(20, 9), font=(SCHRIFT_NAME, 10), borderwidth=1)
style.map("TNotebook.Tab", style.map("TNotebook.Tab",
background=[("selected", self.col_card), background=[("selected", self.col_card),
("active", mischen(accent, bg, 0.12))], ("active", mischen(accent, bg, 0.12))],
lightcolor=[("selected", self.col_card),
("active", mischen(accent, bg, 0.12))],
darkcolor=[("selected", self.col_card),
("active", mischen(accent, bg, 0.12))],
bordercolor=[("selected", accent)],
foreground=[("selected", accent), ("active", fg)], foreground=[("selected", accent), ("active", fg)],
font=[("selected", (SCHRIFT_NAME, 10, "bold"))]) font=[("selected", (SCHRIFT_NAME, 10, "bold"))])
# Kein expand beim ausgewaehlten Reiter: er wuchs dadurch um ein paar # Kein expand beim ausgewaehlten Reiter: er wuchs dadurch um ein paar