506 lines
24 KiB
Python
506 lines
24 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
AzaSettingsMixin – Einstellungsfenster (KG-Modell, Templates, Autotext, Add-ons, etc.).
|
||
"""
|
||
|
||
import tkinter as tk
|
||
from tkinter import ttk, messagebox
|
||
from tkinter.scrolledtext import ScrolledText
|
||
|
||
from aza_audit_log import log_event as _audit_log
|
||
from aza_persistence import (
|
||
load_settings_geometry,
|
||
save_settings_geometry,
|
||
load_templates_text,
|
||
save_templates_text,
|
||
save_autotext,
|
||
save_model,
|
||
_clamp_geometry_str,
|
||
load_signature_name,
|
||
save_signature_name,
|
||
load_user_profile,
|
||
)
|
||
from aza_ui_helpers import add_resize_grip, add_font_scale_control
|
||
from aza_config import MODEL_LABELS, ALLOWED_SUMMARY_MODELS
|
||
|
||
|
||
class AzaSettingsMixin:
|
||
"""Mixin für das Einstellungsfenster."""
|
||
|
||
def _open_settings(self):
|
||
SETTINGS_MIN_W, SETTINGS_MIN_H = 680, 520
|
||
win = tk.Toplevel(self)
|
||
win.title("Einstellungen")
|
||
win.transient(self)
|
||
win.minsize(SETTINGS_MIN_W, SETTINGS_MIN_H)
|
||
win.attributes("-topmost", True)
|
||
if hasattr(self, "_aza_windows"):
|
||
self._aza_windows.add(win)
|
||
self._register_window(win)
|
||
saved_geom = load_settings_geometry()
|
||
if saved_geom:
|
||
try:
|
||
win.geometry(_clamp_geometry_str(saved_geom, SETTINGS_MIN_W, SETTINGS_MIN_H))
|
||
except Exception:
|
||
win.geometry(f"{SETTINGS_MIN_W}x{SETTINGS_MIN_H}")
|
||
if not saved_geom:
|
||
win.geometry(f"{SETTINGS_MIN_W}x{SETTINGS_MIN_H}")
|
||
win.update_idletasks()
|
||
sw = win.winfo_screenwidth()
|
||
sh = win.winfo_screenheight()
|
||
w, h = SETTINGS_MIN_W, SETTINGS_MIN_H
|
||
x = max(0, (sw - w) // 2)
|
||
y = max(0, (sh - h) // 2)
|
||
win.geometry(f"{w}x{h}+{x}+{y}")
|
||
add_resize_grip(win, SETTINGS_MIN_W, SETTINGS_MIN_H)
|
||
add_font_scale_control(win)
|
||
f = ttk.Frame(win, padding=16)
|
||
f.pack(fill="both", expand=True)
|
||
ttk.Label(f, text="KG-Modell:").grid(row=0, column=0, sticky="w", pady=(0, 8))
|
||
display_values = [MODEL_LABELS[m] for m in ALLOWED_SUMMARY_MODELS]
|
||
current = MODEL_LABELS.get(self.model_var.get(), display_values[0])
|
||
model_var_dialog = tk.StringVar(value=current)
|
||
model_box = ttk.Combobox(
|
||
f, textvariable=model_var_dialog, values=display_values, state="readonly", width=42
|
||
)
|
||
model_box.grid(row=0, column=1, sticky="ew", padx=(12, 0), pady=(0, 8))
|
||
f.columnconfigure(1, weight=1)
|
||
|
||
def open_templates():
|
||
tw = tk.Toplevel(win)
|
||
tw.title("Templates")
|
||
tw.transient(win)
|
||
tw.geometry("620x370")
|
||
tw.configure(bg="#B9ECFA")
|
||
tw.minsize(450, 280)
|
||
tw.attributes("-topmost", True)
|
||
self._register_window(tw)
|
||
add_resize_grip(tw, 450, 280)
|
||
add_font_scale_control(tw)
|
||
tf = ttk.Frame(tw, padding=12)
|
||
tf.pack(fill="both", expand=True)
|
||
ttk.Label(tf, text="Kontext für die KI (z. B. „Ich bin ein Dermatologe und schreibe dermatologische Berichte.“). Wird bei der KG-Erstellung berücksichtigt:").pack(anchor="w")
|
||
ttxt = ScrolledText(tf, wrap="word", font=self._text_font, bg="#F5FCFF", height=8)
|
||
ttxt.pack(fill="both", expand=True, pady=(4, 8))
|
||
ttxt.insert("1.0", load_templates_text())
|
||
self._bind_autotext(ttxt)
|
||
btn_f = ttk.Frame(tf)
|
||
btn_f.pack(fill="x")
|
||
def save_and_close():
|
||
save_templates_text(ttxt.get("1.0", "end").strip())
|
||
tw.destroy()
|
||
ttk.Button(btn_f, text="OK", command=save_and_close).pack(side="left", padx=(0, 8))
|
||
ttk.Button(btn_f, text="Abbrechen", command=tw.destroy).pack(side="left")
|
||
|
||
def do_reset():
|
||
save_templates_text("")
|
||
messagebox.showinfo("Reset", "Template-Text wurde zurückgesetzt und ist jetzt leer.")
|
||
|
||
ttk.Button(f, text="Templates", command=open_templates).grid(row=1, column=0, pady=(8, 4), sticky="w")
|
||
ttk.Button(f, text="Reset", command=do_reset).grid(row=1, column=1, pady=(8, 4), sticky="w", padx=(12, 0))
|
||
|
||
start_frame = ttk.LabelFrame(f, text="Startverhalten / Fenster", padding=(10, 5))
|
||
start_frame.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(8, 4))
|
||
|
||
diktat_auto_var = tk.BooleanVar(value=self._autotext_data.get("diktat_auto_start", True))
|
||
ttk.Checkbutton(start_frame, text="Diktat startet sofort (wenn aus: Aufnahme manuell starten)",
|
||
variable=diktat_auto_var).pack(anchor="w", pady=2)
|
||
|
||
notizen_open_on_start_var = tk.BooleanVar(value=self._autotext_data.get("notizen_open_on_start", True))
|
||
ttk.Checkbutton(start_frame, text="Audionotiz beim Programmstart automatisch öffnen",
|
||
variable=notizen_open_on_start_var).pack(anchor="w", pady=2)
|
||
|
||
kommentare_auto_var = tk.BooleanVar(value=self._autotext_data.get("kommentare_auto_open", False))
|
||
ttk.Checkbutton(start_frame, text="Kommentare-Fenster beim Programmstart automatisch öffnen",
|
||
variable=kommentare_auto_var).pack(anchor="w", pady=2)
|
||
|
||
empfang_auto_var = tk.BooleanVar(value=self._autotext_data.get("empfang_auto_open", False))
|
||
ttk.Checkbutton(start_frame, text="Empfang-Fenster beim Programmstart automatisch öffnen",
|
||
variable=empfang_auto_var).pack(anchor="w", pady=2)
|
||
def _live_textbloecke_visible(*_args):
|
||
vis = bool(textbloecke_visible_var.get())
|
||
self._autotext_data["textbloecke_visible"] = vis
|
||
try:
|
||
if vis:
|
||
self._textbloecke_container.pack(fill="x", before=self._textbloecke_anchor)
|
||
else:
|
||
self._textbloecke_container.pack_forget()
|
||
self.update_idletasks()
|
||
except Exception:
|
||
pass
|
||
|
||
textbloecke_visible_var = tk.BooleanVar(value=self._autotext_data.get("textbloecke_visible", True))
|
||
cb_textbloecke = ttk.Checkbutton(f, text="Textblöcke anzeigen (Inhalt bleibt gespeichert, wenn ausgeblendet)",
|
||
variable=textbloecke_visible_var, command=_live_textbloecke_visible)
|
||
cb_textbloecke.grid(row=3, column=0, columnspan=2, sticky="w", pady=(4, 2))
|
||
def _live_addon_visible(*_args):
|
||
vis = bool(addon_visible_var.get())
|
||
self._autotext_data["addon_visible"] = vis
|
||
try:
|
||
if vis:
|
||
self._addon_container.pack(fill="x", before=self._addon_anchor)
|
||
self._update_addon_buttons_visibility()
|
||
else:
|
||
self._addon_container.pack_forget()
|
||
self.update_idletasks()
|
||
except Exception:
|
||
pass
|
||
|
||
addon_visible_var = tk.BooleanVar(value=self._autotext_data.get("addon_visible", True))
|
||
cb_addon = ttk.Checkbutton(f, text="Add-ons anzeigen", variable=addon_visible_var,
|
||
command=_live_addon_visible)
|
||
cb_addon.grid(row=4, column=0, columnspan=2, sticky="w", pady=(4, 2))
|
||
|
||
def _live_logo_visible(*_args):
|
||
vis = bool(logo_visible_var.get())
|
||
self._autotext_data["logo_visible"] = vis
|
||
try:
|
||
if vis:
|
||
self._logo_frame.place(relx=0.01, rely=0.97, anchor="sw")
|
||
else:
|
||
self._logo_frame.place_forget()
|
||
self.update_idletasks()
|
||
except Exception:
|
||
pass
|
||
|
||
logo_visible_var = tk.BooleanVar(value=self._autotext_data.get("logo_visible", True))
|
||
cb_logo = ttk.Checkbutton(f, text="Logo anzeigen (Klick auf Logo startet/stoppt Aufnahme)",
|
||
variable=logo_visible_var, command=_live_logo_visible)
|
||
cb_logo.grid(row=4, column=1, sticky="w", pady=(4, 2), padx=(12, 0))
|
||
|
||
# Unterkategorie: Welche Add-on-Buttons sollen angezeigt werden?
|
||
addon_buttons_frame = ttk.LabelFrame(f, text="Welche Add-on-Buttons anzeigen?", padding=(10, 5))
|
||
addon_buttons_frame.grid(row=5, column=0, columnspan=2, sticky="ew", pady=(8, 4))
|
||
|
||
addon_buttons = self._autotext_data.get("addon_buttons", {})
|
||
addon_button_vars = {}
|
||
|
||
addon_button_options = [
|
||
("uebersetzer", "Übersetzer (provisorisch)"),
|
||
("email", "E-Mail"),
|
||
("autotext", "Autotext"),
|
||
("whatsapp", "WhatsApp"),
|
||
("docapp", "MedWork"),
|
||
("todo", "To-do"),
|
||
("macro", "Makro starten"),
|
||
("kongresse", "Kongresse"),
|
||
("news", "News"),
|
||
("empfang", "An Empfang senden"),
|
||
]
|
||
|
||
todo_auto_open_var = tk.BooleanVar(
|
||
value=self._autotext_data.get("todo_auto_open", True))
|
||
|
||
def _live_addon_toggle(*_args):
|
||
self._autotext_data["addon_buttons"] = {
|
||
bid: bool(v.get()) for bid, v in addon_button_vars.items()
|
||
}
|
||
try:
|
||
self._update_addon_buttons_visibility()
|
||
except Exception:
|
||
pass
|
||
|
||
grid_row = 0
|
||
for button_id, label in addon_button_options:
|
||
var = tk.BooleanVar(value=addon_buttons.get(button_id, True))
|
||
addon_button_vars[button_id] = var
|
||
cb = ttk.Checkbutton(addon_buttons_frame, text=label, variable=var,
|
||
command=_live_addon_toggle)
|
||
cb.grid(row=grid_row, column=0, sticky="w", padx=10, pady=2)
|
||
grid_row += 1
|
||
if button_id == "todo":
|
||
cb_auto = ttk.Checkbutton(
|
||
addon_buttons_frame,
|
||
text=" ↳ To-do beim Start automatisch öffnen",
|
||
variable=todo_auto_open_var)
|
||
cb_auto.grid(row=grid_row, column=0, sticky="w", padx=10, pady=(0, 2))
|
||
grid_row += 1
|
||
|
||
kg_auto_delete_var = tk.BooleanVar(value=self._autotext_data.get("kg_auto_delete_old", False))
|
||
cb_kg_auto = ttk.Checkbutton(f, text="KG-Einträge älter als 2 Wochen automatisch löschen (Speicher schonen)", variable=kg_auto_delete_var)
|
||
cb_kg_auto.grid(row=6, column=0, columnspan=2, sticky="w", pady=(4, 2))
|
||
|
||
# Statusanzeige-Farbe
|
||
status_color_frame = ttk.LabelFrame(f, text="Statusanzeige", padding=(10, 5))
|
||
status_color_frame.grid(row=7, column=0, columnspan=2, sticky="ew", pady=(8, 4))
|
||
_status_color_options = {"Standard (Orange)": "#BD4500", "Blau": "#1a4d6d", "Ausblenden": "hidden"}
|
||
_current_sc = self._autotext_data.get("status_color", "#BD4500")
|
||
_sc_label = "Standard (Orange)"
|
||
for _lbl, _val in _status_color_options.items():
|
||
if _val == _current_sc:
|
||
_sc_label = _lbl
|
||
break
|
||
status_color_var = tk.StringVar(value=_sc_label)
|
||
|
||
def _live_status_color(*_args):
|
||
sc_sel = status_color_var.get()
|
||
sc_v = _status_color_options.get(sc_sel, "#BD4500")
|
||
self._autotext_data["status_color"] = sc_v
|
||
try:
|
||
self._apply_status_color()
|
||
except Exception:
|
||
pass
|
||
|
||
for sc_col, (sc_label, sc_val) in enumerate(_status_color_options.items()):
|
||
ttk.Radiobutton(status_color_frame, text=sc_label, variable=status_color_var,
|
||
value=sc_label, command=_live_status_color).grid(row=0, column=sc_col, padx=8, pady=2, sticky="w")
|
||
|
||
autotext_var = tk.BooleanVar(value=self._autotext_data.get("enabled", True))
|
||
cb_autotext = ttk.Checkbutton(f, text="Autotext (Abkürzungen z. B. „mfg“ → „mit freundlichen Grüßen“)", variable=autotext_var)
|
||
cb_autotext.grid(row=8, column=0, columnspan=2, sticky="w", pady=(4, 2))
|
||
def open_autotext_manage():
|
||
self._open_autotext_dialog(win)
|
||
ttk.Button(f, text="Autotext verwalten", command=open_autotext_manage).grid(row=9, column=0, pady=(2, 4), sticky="w")
|
||
|
||
autocopy_var = tk.BooleanVar(
|
||
value=self._autotext_data.get("autocopy_after_diktat", True)
|
||
)
|
||
cb_autocopy = ttk.Checkbutton(
|
||
f,
|
||
text="Autocopy: Nach Diktat/Transkription automatisch in Zwischenablage kopieren",
|
||
variable=autocopy_var,
|
||
)
|
||
cb_autocopy.grid(row=10, column=0, columnspan=2, sticky="w", pady=(4, 2))
|
||
|
||
if not hasattr(self, "_rclick_paste_var"):
|
||
self._rclick_paste_var = tk.BooleanVar(
|
||
value=bool(self._autotext_data.get("global_right_click_paste", True)))
|
||
|
||
cb_global_right_click = ttk.Checkbutton(
|
||
f,
|
||
text="Global: Rechtsklick fügt direkt ein (ohne Kontextmenü, nur externe Apps)",
|
||
variable=self._rclick_paste_var,
|
||
command=self._toggle_rclick_paste,
|
||
)
|
||
cb_global_right_click.grid(row=11, column=0, columnspan=2, sticky="w", pady=(4, 2))
|
||
|
||
sig_frame = ttk.LabelFrame(f, text="Unterschrift / Signatur", padding=(10, 5))
|
||
sig_frame.grid(row=12, column=0, columnspan=2, sticky="ew", pady=(8, 4))
|
||
sig_frame.columnconfigure(1, weight=1)
|
||
|
||
profile_name = self._user_profile.get("name", "")
|
||
current_sig = load_signature_name(fallback_to_profile=False)
|
||
use_profile = not bool(current_sig)
|
||
|
||
sig_auto_var = tk.BooleanVar(value=use_profile)
|
||
sig_name_var = tk.StringVar(value=current_sig if current_sig else profile_name)
|
||
|
||
cb_sig_auto = ttk.Checkbutton(sig_frame,
|
||
text=f"Profilname verwenden: {profile_name}" if profile_name else "Profilname verwenden",
|
||
variable=sig_auto_var)
|
||
cb_sig_auto.grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 4))
|
||
|
||
ttk.Label(sig_frame, text="Abweichender Name:").grid(row=1, column=0, sticky="w", padx=(0, 8))
|
||
ent_sig = ttk.Entry(sig_frame, textvariable=sig_name_var, width=36)
|
||
ent_sig.grid(row=1, column=1, sticky="ew", pady=(0, 2))
|
||
|
||
def _update_sig_entry(*_):
|
||
if sig_auto_var.get():
|
||
ent_sig.configure(state="disabled")
|
||
sig_name_var.set(profile_name)
|
||
else:
|
||
ent_sig.configure(state="normal")
|
||
sig_auto_var.trace_add("write", _update_sig_entry)
|
||
_update_sig_entry()
|
||
|
||
self._sig_auto_var = sig_auto_var
|
||
self._sig_name_var = sig_name_var
|
||
|
||
# --- Audio-Test ---
|
||
audio_frame = ttk.LabelFrame(f, text="Audio / Mikrofon", padding=(10, 5))
|
||
audio_frame.grid(row=13, column=0, columnspan=2, sticky="ew", pady=(8, 4))
|
||
|
||
audio_status_var = tk.StringVar(value="")
|
||
|
||
def _run_audio_test():
|
||
audio_status_var.set("Test läuft …")
|
||
win.update_idletasks()
|
||
try:
|
||
from aza_audio import test_audio_device
|
||
result = test_audio_device(duration_sec=1.5)
|
||
if result["ok"]:
|
||
audio_status_var.set("✓ " + result["message"])
|
||
else:
|
||
audio_status_var.set("✗ " + result["message"])
|
||
except Exception as exc:
|
||
audio_status_var.set(f"✗ Fehler: {exc}")
|
||
|
||
ttk.Button(audio_frame, text="Audio-Test starten",
|
||
command=_run_audio_test).pack(side="left", padx=(0, 12))
|
||
tk.Label(audio_frame, textvariable=audio_status_var,
|
||
font=("Segoe UI", 9), fg="#333", bg="#F0F0F0",
|
||
wraplength=400, justify="left").pack(side="left", fill="x", expand=True)
|
||
|
||
legal_frame = ttk.LabelFrame(f, text="Datenschutz & Recht", padding=(10, 5))
|
||
legal_frame.grid(row=14, column=0, columnspan=2, sticky="ew", pady=(8, 4))
|
||
ttk.Button(legal_frame, text="Datenschutzerklärung anzeigen",
|
||
command=lambda: self._show_legal_text(win, "Datenschutzerklärung", "privacy_policy.md")
|
||
).grid(row=0, column=0, padx=(0, 8), pady=2, sticky="w")
|
||
ttk.Button(legal_frame, text="KI-Einwilligung anzeigen",
|
||
command=lambda: self._show_legal_text(win, "KI-Einwilligung", "ai_consent.md")
|
||
).grid(row=0, column=1, padx=0, pady=2, sticky="w")
|
||
|
||
from aza_consent import get_consent_status, record_revoke, has_valid_consent, record_consent, export_consent_log
|
||
uid = self._user_profile.get("name", "default")
|
||
consent_ok = has_valid_consent(uid)
|
||
consent_status_var = tk.StringVar(
|
||
value=f"KI-Einwilligung: {'Erteilt' if consent_ok else 'Nicht erteilt / widerrufen'}")
|
||
ttk.Label(legal_frame, textvariable=consent_status_var).grid(
|
||
row=1, column=0, columnspan=2, sticky="w", pady=(6, 2))
|
||
|
||
def toggle_consent():
|
||
nonlocal consent_ok
|
||
_uid = self._user_profile.get("name", "default")
|
||
if has_valid_consent(_uid):
|
||
if messagebox.askyesno("Einwilligung widerrufen",
|
||
"Möchten Sie Ihre KI-Einwilligung widerrufen?\n\n"
|
||
"KI-Funktionen (Transkription, KG-Erstellung,\n"
|
||
"Interaktionsprüfung) werden danach gesperrt.",
|
||
parent=win):
|
||
record_revoke(_uid, source="ui")
|
||
_audit_log("CONSENT_REVOKE", _uid)
|
||
consent_ok = False
|
||
consent_status_var.set("KI-Einwilligung: Widerrufen")
|
||
btn_consent.configure(text="KI-Einwilligung erteilen")
|
||
messagebox.showinfo("Widerruf", "Ihre KI-Einwilligung wurde widerrufen und protokolliert.", parent=win)
|
||
else:
|
||
if self._check_ai_consent():
|
||
consent_ok = True
|
||
consent_status_var.set("KI-Einwilligung: Erteilt")
|
||
btn_consent.configure(text="KI-Einwilligung widerrufen")
|
||
|
||
btn_consent = ttk.Button(legal_frame,
|
||
text="KI-Einwilligung widerrufen" if consent_ok else "KI-Einwilligung erteilen",
|
||
command=toggle_consent)
|
||
btn_consent.grid(row=2, column=0, padx=(0, 8), pady=2, sticky="w")
|
||
|
||
def do_export():
|
||
from aza_audit_log import export_audit_log
|
||
try:
|
||
path_consent = export_consent_log()
|
||
path_audit = export_audit_log()
|
||
_audit_log("EXPORT", uid, detail="consent+audit log")
|
||
messagebox.showinfo("Export",
|
||
f"Consent-Log exportiert:\n{path_consent}\n\n"
|
||
f"Audit-Log exportiert:\n{path_audit}", parent=win)
|
||
except Exception as e:
|
||
messagebox.showerror("Export-Fehler", str(e), parent=win)
|
||
|
||
ttk.Button(legal_frame, text="Logs exportieren (Audit)",
|
||
command=do_export).grid(row=2, column=1, padx=0, pady=2, sticky="w")
|
||
|
||
def save_and_close():
|
||
try:
|
||
save_settings_geometry(win.geometry())
|
||
except Exception:
|
||
pass
|
||
if hasattr(self, "_aza_windows"):
|
||
self._aza_windows.discard(win)
|
||
win.destroy()
|
||
|
||
def on_ok():
|
||
selected_label = model_var_dialog.get().strip()
|
||
for model_id, label in MODEL_LABELS.items():
|
||
if label == selected_label:
|
||
self.model_var.set(model_id)
|
||
save_model(model_id)
|
||
break
|
||
self._autotext_data["enabled"] = bool(autotext_var.get())
|
||
self._autotext_data["diktat_auto_start"] = bool(diktat_auto_var.get())
|
||
self._autotext_data["notizen_open_on_start"] = bool(notizen_open_on_start_var.get())
|
||
self._autotext_data["textbloecke_visible"] = bool(textbloecke_visible_var.get())
|
||
self._autotext_data["addon_visible"] = bool(addon_visible_var.get())
|
||
|
||
# Speichere die einzelnen Button-Einstellungen
|
||
self._autotext_data["addon_buttons"] = {
|
||
button_id: bool(var.get())
|
||
for button_id, var in addon_button_vars.items()
|
||
}
|
||
|
||
self._autotext_data["kg_auto_delete_old"] = bool(kg_auto_delete_var.get())
|
||
self._autotext_data["todo_auto_open"] = bool(todo_auto_open_var.get())
|
||
self._autotext_data["autocopy_after_diktat"] = bool(autocopy_var.get())
|
||
self._autotext_data["global_right_click_paste"] = bool(self._rclick_paste_var.get())
|
||
self._autotext_data["kommentare_auto_open"] = bool(kommentare_auto_var.get())
|
||
self._autotext_data["empfang_auto_open"] = bool(empfang_auto_var.get())
|
||
self._autotext_data["logo_visible"] = bool(logo_visible_var.get())
|
||
|
||
if self._sig_auto_var.get():
|
||
save_signature_name("")
|
||
else:
|
||
save_signature_name(self._sig_name_var.get().strip())
|
||
|
||
# Statusanzeige-Farbe speichern
|
||
sc_selected = status_color_var.get()
|
||
sc_value = _status_color_options.get(sc_selected, "#BD4500")
|
||
self._autotext_data["status_color"] = sc_value
|
||
|
||
save_autotext(self._autotext_data)
|
||
save_and_close()
|
||
# UI-Updates nach Schließen des Einstellungsfensters (verhindert Hang)
|
||
def _apply_ui():
|
||
try:
|
||
if self._autotext_data["textbloecke_visible"]:
|
||
self._textbloecke_container.pack(fill="x", before=self._textbloecke_anchor)
|
||
else:
|
||
self._textbloecke_container.pack_forget()
|
||
if self._autotext_data["addon_visible"]:
|
||
self._addon_container.pack(fill="x", before=self._addon_anchor)
|
||
self._update_addon_buttons_visibility()
|
||
self.update_idletasks()
|
||
h = self.winfo_height()
|
||
if h < 500:
|
||
self.geometry(f"{self.winfo_width()}x500")
|
||
else:
|
||
self._addon_container.pack_forget()
|
||
except Exception:
|
||
pass
|
||
try:
|
||
self._apply_status_color()
|
||
except Exception:
|
||
pass
|
||
self.update_idletasks()
|
||
self.after(50, _apply_ui)
|
||
|
||
win.protocol("WM_DELETE_WINDOW", save_and_close)
|
||
ttk.Button(f, text="OK", command=on_ok).grid(row=15, column=0, columnspan=2, pady=(12, 0))
|
||
win.focus_set()
|
||
|
||
def _show_legal_text(self, parent, title: str, filename: str):
|
||
"""Zeigt einen Rechtstext (Markdown) in einem Lesefenster an."""
|
||
import os
|
||
legal_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "legal")
|
||
filepath = os.path.join(legal_dir, filename)
|
||
|
||
try:
|
||
with open(filepath, "r", encoding="utf-8") as fh:
|
||
content = fh.read()
|
||
except FileNotFoundError:
|
||
messagebox.showerror("Fehler", f"Datei nicht gefunden:\n{filepath}", parent=parent)
|
||
return
|
||
except OSError as e:
|
||
messagebox.showerror("Fehler", f"Datei konnte nicht gelesen werden:\n{e}", parent=parent)
|
||
return
|
||
|
||
tw = tk.Toplevel(parent)
|
||
tw.title(title)
|
||
tw.transient(parent)
|
||
tw.geometry("720x600")
|
||
tw.minsize(500, 400)
|
||
tw.attributes("-topmost", True)
|
||
self._register_window(tw)
|
||
|
||
from aza_ui_helpers import add_resize_grip, add_font_scale_control
|
||
add_resize_grip(tw, 500, 400)
|
||
add_font_scale_control(tw)
|
||
|
||
frame = ttk.Frame(tw, padding=12)
|
||
frame.pack(fill="both", expand=True)
|
||
|
||
txt = ScrolledText(frame, wrap="word", font=("Segoe UI", 10), bg="#FAFAFA")
|
||
txt.pack(fill="both", expand=True, pady=(0, 8))
|
||
txt.insert("1.0", content)
|
||
txt.configure(state="disabled")
|
||
|
||
ttk.Button(frame, text="Schliessen", command=tw.destroy).pack(anchor="e")
|