Eigene Meldungsfenster, aktiver Reiter hervorgehoben, Pruefschalter als Funktion
Sein Fund: "der ohne gemeinsamen ordner popup ist noch hell" und "er hebt den aktiven tab nicht hervor, der inaktive ist der hervorgehobene". - Meldungen und Ja/Nein-Fragen kommen jetzt aus eigenen Fenstern im Theme (MeldungMixin). messagebox sind Fenster von WINDOWS - sie bleiben hell, egal was das Programm einstellt. Dieselbe Erfahrung steckt schon in der eigenen Menueleiste. 49 Aufrufe umgestellt; das Ordner-Fenster meldet ueber das Hauptfenster mit. ⚠️ Im Pruflauf laeuft beides weiter ueber messagebox - dort haengen die Pruefstaende ihre Antworten ein. Sonst warten sie auf einen Klick. - Reiter: der OFFENE ist jetzt der hellere (Kachelfarbe), die ruhenden liegen auf dem dunklen Grund. Vorher war es andersherum. - config.TESTLAUF ist eine FUNKTION geworden: Pruefstaende importieren config oft, bevor sie CAMPINGHOF_TEST setzen - der einmal berechnete Wert stand dann den ganzen Lauf auf False, und das Programm oeffnete echte Dialoge, auf die niemand klickt. Genau daran hing pruef_api. - Der Erststart-Dialog hat einen eigenen Schalter (_erststart_zeigen), statt am ganzen Pruefschalter zu haengen: ein Pruefstand kann IHN zeigen, ohne dass alle anderen Meldungen wieder echte Fenster aufmachen. - Pruefstand-Fenster stehen nicht mehr in der Taskleiste (-toolwindow): sie liegen neben dem Bildschirm, und ein Eintrag, den man anklickt, ohne dass etwas erscheint, ist schlimmer als gar keiner. - pruef_nummernsperre drueckt jetzt den Radioknopf "Meine Zaehlung weiterfuehren", statt nur ins Zahlenfeld zu schreiben - das Feld ist ohne diesen Klick gesperrt, wie beim Benutzer auch. 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:
parent
5ebb02b267
commit
2e3a191f19
8 changed files with 229 additions and 74 deletions
106
theme.py
106
theme.py
|
|
@ -8,6 +8,7 @@ baut das Programm Menueleiste und Dropdowns selbst; hier stehen nur die Farben.
|
|||
import sys
|
||||
import tkinter as tk
|
||||
from tkinter import font as tkfont
|
||||
from tkinter import messagebox
|
||||
from tkinter import ttk
|
||||
|
||||
import config
|
||||
|
|
@ -264,11 +265,15 @@ class ThemeMixin:
|
|||
# Akzentfarbe, die ruhenden bleiben zurueckgenommen.
|
||||
style.configure("TNotebook", background=bg, bordercolor=self.col_line,
|
||||
tabmargins=(2, 4, 2, 0), borderwidth=0)
|
||||
style.configure("TNotebook.Tab", background=self.col_field,
|
||||
# Der OFFENE Reiter gehoert zur Kachel darunter und ist deshalb heller
|
||||
# als die ruhenden. Vorher war es andersherum: die ruhenden lagen auf der
|
||||
# helleren Flaeche und sahen aus, als waeren sie ausgewaehlt.
|
||||
style.configure("TNotebook.Tab", background=bg,
|
||||
foreground=self.col_muted, bordercolor=self.col_line,
|
||||
padding=(20, 9), font=(SCHRIFT_NAME, 10), borderwidth=0)
|
||||
style.map("TNotebook.Tab",
|
||||
background=[("selected", self.col_card), ("active", sel)],
|
||||
background=[("selected", self.col_card),
|
||||
("active", mischen(accent, bg, 0.12))],
|
||||
foreground=[("selected", accent), ("active", fg)],
|
||||
font=[("selected", (SCHRIFT_NAME, 10, "bold"))])
|
||||
# Kein expand beim ausgewaehlten Reiter: er wuchs dadurch um ein paar
|
||||
|
|
@ -286,12 +291,16 @@ class ThemeMixin:
|
|||
und Tastatur fest, waehrend nebenan jemand schreibt.
|
||||
"""
|
||||
d.configure(bg=self.col_bg)
|
||||
if config.TESTLAUF:
|
||||
if config.testlauf():
|
||||
# Wie das Hauptfenster: neben den Bildschirm, und ohne grab_set -
|
||||
# ein Dialog, der Maus und Tastatur festhaelt, blockiert den
|
||||
# Menschen, der nebenher schreibt.
|
||||
d.geometry(f"+{d.winfo_screenwidth() + 20}+40")
|
||||
d.grab_set = lambda *a, **k: None
|
||||
try:
|
||||
d.attributes("-toolwindow", True) # nicht in die Taskleiste
|
||||
except tk.TclError:
|
||||
pass
|
||||
# erst nach dem Anzeigen setzen, sonst greift DWM noch nicht
|
||||
d.after(20, lambda: self._dark_titlebar(d))
|
||||
|
||||
|
|
@ -312,3 +321,94 @@ class ThemeMixin:
|
|||
ctypes.windll.user32.SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP)
|
||||
except Exception as e: # noqa: BLE001
|
||||
print("[theme] Titelleiste:", e)
|
||||
|
||||
|
||||
class MeldungMixin:
|
||||
"""Eigene Meldungs- und Ja/Nein-Fenster im Theme des Programms.
|
||||
|
||||
Warum nicht `messagebox`: das sind Fenster von Windows selbst. Sie sind
|
||||
immer hell, egal was das Programm einstellt - mitten in einer dunklen
|
||||
Oberflaeche blendet das und sieht nach Fremdkoerper aus. Dieselbe Erfahrung
|
||||
steckt schon in der eigenen Menueleiste weiter oben.
|
||||
|
||||
⚠️ Im PRUEFLAUF geht beides weiter ueber `messagebox`. Die Pruefstaende
|
||||
haengen ihre Antworten dort ein ("auf diese Frage antworte Ja") und lesen
|
||||
dort mit, was das Programm gemeldet hat. Wuerde hier ein eigenes Fenster
|
||||
aufgehen, warteten sie auf einen Klick, den niemand macht. Geprueft wird
|
||||
damit derselbe Weg - nur die Anzeige unterscheidet sich.
|
||||
"""
|
||||
|
||||
# Die drei Sorten Meldung. `parent=`, `icon=` und `default=` kommen aus den
|
||||
# frueheren messagebox-Aufrufen mit - sie werden geschluckt, damit an den
|
||||
# Aufrufstellen nichts umgeschrieben werden muss.
|
||||
def melden_info(self, titel, text="", **_kw):
|
||||
return self.melden(titel, text, "info")
|
||||
|
||||
def melden_warnung(self, titel, text="", **_kw):
|
||||
return self.melden(titel, text, "warnung")
|
||||
|
||||
def melden_fehler(self, titel, text="", **_kw):
|
||||
return self.melden(titel, text, "fehler")
|
||||
|
||||
def melden(self, titel, text, art="warnung"):
|
||||
"""Eine Meldung mit einem OK-Knopf."""
|
||||
if config.testlauf():
|
||||
{"info": messagebox.showinfo,
|
||||
"warnung": messagebox.showwarning,
|
||||
"fehler": messagebox.showerror}.get(art, messagebox.showwarning)(titel, text)
|
||||
return None
|
||||
return self._meldungsfenster(titel, text, art, knoepfe=None)
|
||||
|
||||
def frage(self, titel, text="", ja="Ja", nein="Nein", vorgabe=True, **_kw):
|
||||
"""Ja/Nein. Gibt True oder False zurueck - wie askyesno."""
|
||||
if config.testlauf():
|
||||
return bool(messagebox.askyesno(titel, text))
|
||||
return bool(self._meldungsfenster(titel, text, "frage",
|
||||
knoepfe=(ja, nein), vorgabe=vorgabe))
|
||||
|
||||
def _meldungsfenster(self, titel, text, art, knoepfe=None, vorgabe=True):
|
||||
farbe = {"fehler": self.col_gefahr, "warnung": self.col_gefahr,
|
||||
"frage": self.col_accent}.get(art, self.col_accent)
|
||||
d = tk.Toplevel(self)
|
||||
d.title(titel)
|
||||
d.transient(self)
|
||||
d.resizable(False, False)
|
||||
self._style_dialog(d)
|
||||
antwort = {"wert": False}
|
||||
|
||||
rahmen = ttk.Frame(d, padding=20)
|
||||
rahmen.pack(fill="both", expand=True)
|
||||
tk.Label(rahmen, text=titel, bg=self.col_bg, fg=farbe, justify="left",
|
||||
font=(SCHRIFT_NAME, 12, "bold")).pack(anchor="w")
|
||||
tk.Label(rahmen, text=text, bg=self.col_bg, fg=self.col_fg, justify="left",
|
||||
wraplength=460, font=(SCHRIFT_NAME, 10)).pack(anchor="w", pady=(10, 0))
|
||||
|
||||
leiste = ttk.Frame(rahmen)
|
||||
leiste.pack(fill="x", pady=(20, 0))
|
||||
|
||||
def schliessen(wert):
|
||||
antwort["wert"] = wert
|
||||
d.destroy()
|
||||
|
||||
if knoepfe:
|
||||
ja, nein = knoepfe
|
||||
ttk.Button(leiste, text=ja, style="Haupt.TButton",
|
||||
command=lambda: schliessen(True)).pack(side="right")
|
||||
ttk.Button(leiste, text=nein,
|
||||
command=lambda: schliessen(False)).pack(side="right", padx=(0, 8))
|
||||
d.bind("<Return>", lambda _e: schliessen(vorgabe))
|
||||
else:
|
||||
ttk.Button(leiste, text="OK", style="Haupt.TButton",
|
||||
command=lambda: schliessen(True)).pack(side="right")
|
||||
d.bind("<Return>", lambda _e: schliessen(True))
|
||||
d.bind("<Escape>", lambda _e: schliessen(False))
|
||||
d.protocol("WM_DELETE_WINDOW", lambda: schliessen(False))
|
||||
|
||||
d.update_idletasks()
|
||||
x = self.winfo_rootx() + max(0, (self.winfo_width() - d.winfo_reqwidth()) // 2)
|
||||
y = self.winfo_rooty() + 140
|
||||
d.geometry(f"+{max(0, x)}+{max(0, y)}")
|
||||
d.grab_set()
|
||||
d.focus_set()
|
||||
self.wait_window(d)
|
||||
return antwort["wert"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue