Files
aza/AzA march 2026 - Kopie (11)/aza_ordner_mixin.py
2026-04-16 13:32:32 +02:00

336 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
AzaOrdnerMixin Ordner-Fenster (Ablage: KG, Briefe, Rezepte, KOGU, Diktat; Export/Import).
"""
import os
import tkinter as tk
from tkinter import ttk, messagebox
from aza_persistence import (
ensure_ablage_dirs,
_ablage_base_path,
load_ordner_geometry,
save_ordner_geometry,
list_ablage_files,
get_ablage_content,
save_to_ablage,
count_entries_older_than,
delete_entries_older_than,
save_autotext,
_clamp_geometry_str,
)
from aza_ui_helpers import center_window, add_resize_grip, add_font_scale_control, RoundedButton
from aza_config import ABLAGE_SUBFOLDERS
class AzaOrdnerMixin:
"""Mixin für das Ordner-/Ablage-Fenster (Speichern, Laden, Export/Import)."""
def open_ordner_window(self):
"""Fenster für Ablage: KG, Briefe, Rezepte, Kostengutsprachen in Unterordnern; Export/Import. Bleibt sichtbar bis es geschlossen wird."""
ensure_ablage_dirs()
base_path = _ablage_base_path()
ORDNER_MIN_W, ORDNER_MIN_H = 750, 600
win = tk.Toplevel(self)
win.title("Ordner Ablage & Export/Import")
win.minsize(ORDNER_MIN_W, ORDNER_MIN_H)
win.configure(bg="#B9ECFA")
win.attributes("-topmost", True)
if hasattr(self, "_aza_windows"):
self._aza_windows.add(win)
self._register_window(win)
# Fensterposition: gespeichert laden oder zentrieren
saved_geom = load_ordner_geometry()
if saved_geom:
try:
win.geometry(_clamp_geometry_str(saved_geom, ORDNER_MIN_W, ORDNER_MIN_H))
except Exception:
win.geometry("800x650")
center_window(win, 800, 650)
else:
# Keine gespeicherte Position → zentrieren
win.geometry("640x500")
center_window(win, 640, 500)
def save_ordner_geom():
try:
save_ordner_geometry(win.geometry())
except Exception:
pass
_ordner_geom_after_id = [None]
def on_ordner_configure(e):
if e.widget is win and _ordner_geom_after_id[0]:
self.after_cancel(_ordner_geom_after_id[0])
if e.widget is win:
_ordner_geom_after_id[0] = self.after(400, save_ordner_geom)
win.bind("<Configure>", on_ordner_configure)
def on_ordner_close():
try:
save_ordner_geometry(win.geometry())
except Exception:
pass
if hasattr(self, "_aza_windows"):
self._aza_windows.discard(win)
win.destroy()
win.protocol("WM_DELETE_WINDOW", on_ordner_close)
add_resize_grip(win, ORDNER_MIN_W, ORDNER_MIN_H)
add_font_scale_control(win)
main_f = ttk.Frame(win, padding=12)
main_f.pack(fill="both", expand=True)
ttk.Label(main_f, text=f"Ablage: {base_path}").pack(anchor="w")
auto_delete_var = tk.BooleanVar(
value=bool(getattr(self, "_autotext_data", {}).get("ablage_auto_delete_old", True))
)
cb_auto_delete = ttk.Checkbutton(
main_f,
text="Automatisch löschen nach 2 Wochen (mit Nachfrage)",
variable=auto_delete_var,
)
cb_auto_delete.pack(anchor="w", pady=(6, 2))
def _persist_auto_delete_pref():
try:
if hasattr(self, "_autotext_data"):
self._autotext_data["ablage_auto_delete_old"] = bool(auto_delete_var.get())
save_autotext(self._autotext_data)
except Exception:
pass
cb_auto_delete.configure(command=_persist_auto_delete_pref)
# ─── Tab-Leiste (blaues Design) ───
_tab_style_active = {"bg": "#E8F4FA", "fg": "#1a4d6d", "font": ("Segoe UI", 10, "bold")}
_tab_style_inactive = {"bg": "#A8D8E8", "fg": "#5A90B0", "font": ("Segoe UI", 10)}
tab_bar = tk.Frame(main_f, bg="#A8D8E8")
tab_bar.pack(fill="x", pady=(8, 0))
_ordner_pages = {}
_ordner_tab_btns = {}
_active_ordner_tab = [None]
for cat in ABLAGE_SUBFOLDERS:
page = tk.Frame(main_f, bg="#E8F4FA")
_ordner_pages[cat] = page
style = _tab_style_active if _active_ordner_tab[0] is None else _tab_style_inactive
if _active_ordner_tab[0] is None:
_active_ordner_tab[0] = cat
btn = tk.Label(tab_bar, text=cat, cursor="hand2", padx=14, pady=5, **style)
btn.pack(side="left")
_ordner_tab_btns[cat] = btn
def _switch_ordner_tab(tab_name):
_active_ordner_tab[0] = tab_name
for key, btn in _ordner_tab_btns.items():
btn.configure(**(_tab_style_active if key == tab_name else _tab_style_inactive))
for key, page in _ordner_pages.items():
page.pack_forget()
_ordner_pages[tab_name].pack(fill="both", expand=True)
for cat in ABLAGE_SUBFOLDERS:
_ordner_tab_btns[cat].bind("<Button-1>", lambda e, c=cat: _switch_ordner_tab(c))
_ordner_pages[_active_ordner_tab[0]].pack(fill="both", expand=True)
def refresh_list(listbox, category):
listbox.delete(0, "end")
for f in list_ablage_files(category):
display_name = f[:-4] if (f and str(f).endswith(".txt")) else f
listbox.insert("end", display_name)
def save_current(category):
if category == "KG":
content = self.txt_output.get("1.0", "end").strip()
if not content:
messagebox.showinfo("Hinweis", "Keine Krankengeschichte zum Speichern.")
return
elif category == "Briefe":
content = self._last_brief_text
if not content:
messagebox.showinfo("Hinweis", "Zuerst einen Brief erstellen (Button Brief).")
return
elif category == "Rezepte":
content = self._last_rezept_text
if not content:
messagebox.showinfo("Hinweis", "Zuerst ein Rezept erstellen (Button Rezept).")
return
elif category == "Kostengutsprachen":
content = self._last_kogu_text
if not content:
messagebox.showinfo("Hinweis", "Zuerst eine Kostengutsprache erstellen (Button KOGU).")
return
elif category == "Diktat":
content = self.txt_transcript.get("1.0", "end").strip()
if not content:
messagebox.showinfo("Hinweis", "Kein Transkript zum Speichern vorhanden.")
return
elif category == "Transkript":
content = self.txt_transcript.get("1.0", "end").strip()
if not content:
messagebox.showinfo("Hinweis", "Kein Transkript zum Speichern vorhanden.")
return
else:
return
try:
path = save_to_ablage(category, content)
if path:
messagebox.showinfo("Gespeichert", f"Gespeichert unter:\n{path}")
for lb in listboxes:
refresh_list(lb["listbox"], lb["category"])
else:
messagebox.showwarning("Hinweis", "Nichts gespeichert (Inhalt war leer).")
except Exception as e:
messagebox.showerror("Fehler", str(e))
def load_file_into_app(category, filename):
content = get_ablage_content(category, filename)
if not content:
messagebox.showinfo("Hinweis", "Datei ist leer oder nicht gefunden.")
return
# Immer in neuem Fenster öffnen; Ordner-Fenster bleibt offen
if category == "KG":
self._show_text_window("KG (geladen)", content, buttons="kg")
self.set_status("KG in neuem Fenster geöffnet.")
elif category == "Briefe":
self._last_brief_text = content
self._show_text_window("Brief (geladen)", content, buttons="brief")
self.set_status("Brief in neuem Fenster geöffnet.")
elif category == "Rezepte":
self._last_rezept_text = content
self._show_text_window("Rezept (geladen)", content, buttons="rezept")
self.set_status("Rezept in neuem Fenster geöffnet.")
elif category == "Kostengutsprachen":
self._last_kogu_text = content
self._show_text_window("KOGU (geladen)", content, buttons="kogu")
self.set_status("KOGU in neuem Fenster geöffnet.")
elif category == "Diktat":
self._show_text_window("Diktat (geladen)", content, buttons=None)
self.set_status("Diktat in neuem Fenster geöffnet.")
elif category == "Transkript":
self._show_text_window("Transkript (geladen)", content, buttons=None)
self.set_status("Transkript in neuem Fenster geöffnet.")
listboxes = []
for cat in ABLAGE_SUBFOLDERS:
page = _ordner_pages[cat]
frame = tk.Frame(page, bg="#E8F4FA", padx=8, pady=4)
frame.pack(fill="both", expand=True)
tk.Label(frame, text="Aktuelles als neue Datei speichern (Nummer + Datum/Uhrzeit):",
font=("Segoe UI", 9), bg="#E8F4FA", fg="#1a4d6d").pack(anchor="w")
btn_row = tk.Frame(frame, bg="#E8F4FA")
btn_row.pack(fill="x", pady=(0, 4))
RoundedButton(
btn_row, "Aktuelles speichern", command=lambda c=cat: save_current(c),
width=140, height=26, canvas_bg="#E8F4FA",
).pack(side="left")
tk.Label(frame, text="Gespeicherte Dateien:",
font=("Segoe UI", 9), bg="#E8F4FA", fg="#1a4d6d").pack(anchor="w", pady=(4, 0))
lb = tk.Listbox(frame, height=10, font=("Segoe UI", 10))
lb.pack(fill="both", expand=True, pady=(2, 4))
refresh_list(lb, cat)
listboxes.append({"listbox": lb, "category": cat})
def on_select(evt, category=cat, listbox=lb):
sel = listbox.curselection()
if not sel:
return
idx = sel[0]
files = list_ablage_files(category)
if 0 <= idx < len(files):
load_file_into_app(category, files[idx])
lb.bind("<Double-Button-1>", on_select)
def load_selected(lbx=lb, c=cat):
sel = lbx.curselection()
if not sel:
messagebox.showinfo("Hinweis", "Bitte eine Datei auswählen.")
return
files = list_ablage_files(c)
if 0 <= sel[0] < len(files):
load_file_into_app(c, files[sel[0]])
RoundedButton(
frame, "Ausgewählte Datei in App laden", command=load_selected,
width=220, height=26, canvas_bg="#E8F4FA",
).pack(fill="x", pady=(0, 4))
def _maybe_cleanup_old_entries():
if not bool(auto_delete_var.get()):
return
total_old = 0
for cat in ABLAGE_SUBFOLDERS:
try:
total_old += int(count_entries_older_than(cat, days=14))
except Exception:
pass
if total_old <= 0:
return
if not messagebox.askyesno(
"Auto-Löschen",
"Sollen Ablage-Dateien älter als 2 Wochen jetzt gelöscht werden?"
):
return
total_deleted = 0
for cat in ABLAGE_SUBFOLDERS:
try:
total_deleted += int(delete_entries_older_than(cat, days=14))
except Exception:
pass
for lb in listboxes:
refresh_list(lb["listbox"], lb["category"])
if total_deleted > 0:
messagebox.showinfo("Auto-Löschen", f"{total_deleted} alte Einträge wurden gelöscht.")
else:
messagebox.showinfo("Auto-Löschen", "Keine Einträge älter als 2 Wochen gefunden.")
# Nachfrage beim Öffnen des Fensters (nur wenn Checkbox aktiv).
win.after(250, _maybe_cleanup_old_entries)
btn_bottom = ttk.Frame(main_f)
btn_bottom.pack(fill="x", pady=(8, 0))
def do_export():
from tkinter import filedialog
import zipfile
dest = filedialog.asksaveasfilename(
title="Ablage exportieren (ZIP)",
defaultextension=".zip",
filetypes=[("ZIP", "*.zip"), ("Alle", "*.*")],
)
if not dest:
return
try:
with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(base_path):
for f in files:
if not f.endswith(".txt"):
continue
path = os.path.join(root, f)
arcname = os.path.relpath(path, base_path)
zf.write(path, arcname)
messagebox.showinfo("Export", f"Exportiert nach:\n{dest}")
except Exception as e:
messagebox.showerror("Export fehlgeschlagen", str(e))
def do_import():
from tkinter import filedialog
import zipfile
src = filedialog.askopenfilename(
title="ZIP importieren (Inhalt in Ablage entpacken)",
filetypes=[("ZIP", "*.zip"), ("Alle", "*.*")],
)
if not src:
return
try:
with zipfile.ZipFile(src, "r") as zf:
zf.extractall(base_path)
messagebox.showinfo("Import", "Import abgeschlossen.")
for lb in listboxes:
refresh_list(lb["listbox"], lb["category"])
except Exception as e:
messagebox.showerror("Import fehlgeschlagen", str(e))
RoundedButton(btn_bottom, "Export (ZIP)", command=do_export, width=120, height=26, canvas_bg="#B9ECFA").pack(side="left", padx=(0, 8))
RoundedButton(btn_bottom, "Import (ZIP)", command=do_import, width=120, height=26, canvas_bg="#B9ECFA").pack(side="left")