# -*- coding: utf-8 -*- """ =============================================================================== nvWindow.py – Tkinter-Vorschlagsfenster für NV_MASTER-Abgleich (LibreOffice) =============================================================================== Zweck: Dieses Skript öffnet ein Tkinter-Fenster, das Live-Vorschläge zu Begriffen aus der Datei NV_MASTER.ods anzeigt. Es ist so ausgelegt, dass es direkt mit LibreOffice Calc über UNO kommunizieren kann – sofern möglich. Robustheit: Das Skript erkennt automatisch, ob UNO verfügbar und kompatibel ist: - wenn ja → direkte Integration mit LibreOffice (bidirektional) - wenn nein → Fallback in isolierten Modus, aber weiterhin lauffähig Alle Ereignisse, Fehler und Systemzustände werden geloggt. Start LibreOffice (empfohlen): soffice --calc --accept="socket,host=localhost,port=2002;urp;" --nologo --norestore & Start der Anwendung: /usr/lib/libreoffice/program/python3 ~/projects/nvWindow/nvWindow.py (oder notfalls:) python3 ~/projects/nvWindow/nvWindow.py Abhängigkeiten: pip install rapidfuzz odfpy =============================================================================== """ import os import sys import traceback import tkinter as tk from datetime import datetime from rapidfuzz import process, fuzz from odf.opendocument import load from odf.table import Table, TableRow, TableCell from odf.text import P # ------------------------------------------------------------------------------ # 1. Verzeichnis-Setup und Logging # ------------------------------------------------------------------------------ BASE_DIR = os.path.expanduser("~/projects/nvWindow") LOG_FILE = os.path.join(BASE_DIR, "nvWindow.log") NV_MASTER_PATH = os.path.join(BASE_DIR, "NV_MASTER.ods") def log(msg): """Schreibt Zeitstempel und Nachricht in die Logdatei.""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") try: with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(f"[{timestamp}] {msg}\n") except Exception: # im absoluten Notfall auf stdout ausweichen print(f"[{timestamp}] {msg}") # ------------------------------------------------------------------------------ # 2. UNO-Initialisierung (robust, mit Fallback) # ------------------------------------------------------------------------------ uno_available = False desktop = None def init_uno(): """Versucht UNO-Bridge zu initialisieren, erkennt Systemumgebung automatisch.""" global uno_available, desktop # bereits aktiv in LibreOffice? (XSCRIPTCONTEXT vorhanden) if "XSCRIPTCONTEXT" in globals(): try: ctx = XSCRIPTCONTEXT.getComponentContext() smgr = ctx.ServiceManager desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) uno_available = True log("UNO erkannt: Skript läuft innerhalb von LibreOffice.") return except Exception as e: log(f"UNO innerhalb LO-Session nicht initialisierbar: {e}") uno_available = False return # außerhalb LibreOffice: versuche Systemintegration try: sys.path.append("/usr/lib/python3/dist-packages") sys.path.append("/usr/lib/libreoffice/program") os.environ["URE_BOOTSTRAP"] = ( "vnd.sun.star.pathname:/usr/lib/libreoffice/program/fundamentalrc" ) import uno from com.sun.star.beans import PropertyValue try: ctx = uno.getComponentContext() smgr = ctx.ServiceManager desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) uno_available = True log("UNO erfolgreich initialisiert – externe Verbindung aktiv.") except Exception as inner: log(f"UNO verfügbar, aber keine Desktop-Instanz: {inner}") uno_available = False except Exception as outer: log(f"UNO-Import fehlgeschlagen: {outer}\n{traceback.format_exc()}") uno_available = False init_uno() # ------------------------------------------------------------------------------ # 3. NV_MASTER laden # ------------------------------------------------------------------------------ def load_nv_master(filepath): """Liest NV_MASTER.ods mit odfpy ein, extrahiert alle Textinhalte.""" entries = [] if not os.path.exists(filepath): log(f"NV_MASTER.ods nicht gefunden: {filepath}") return [] try: doc = load(filepath) for table in doc.getElementsByType(Table): for row in table.getElementsByType(TableRow): cells = row.getElementsByType(TableCell) if not cells: continue cell_text = [] for c in cells: ps = c.getElementsByType(P) for p in ps: if p.firstChild: text = str(p.firstChild.data).strip() if text: cell_text.append(text) if cell_text: entries.append(" ".join(cell_text)) log(f"NV_MASTER geladen – {len(entries)} Begriffe erkannt.") return entries except Exception as e: log(f"Fehler beim Lesen NV_MASTER: {e}\n{traceback.format_exc()}") return [] NV_MASTER_LIST = load_nv_master(NV_MASTER_PATH) if not NV_MASTER_LIST: log("Abbruch: NV_MASTER.ods konnte nicht gelesen werden oder war leer.") sys.exit("NV_MASTER.ods fehlt oder enthält keine Daten.") # ------------------------------------------------------------------------------ # 4. Tkinter-GUI # ------------------------------------------------------------------------------ root = tk.Tk() root.title("Objektbeschreibung – Vorschläge") root.attributes("-topmost", True) root.geometry("420x260+1500+800") root.configure(bg="#202020") label = tk.Label( root, text="Live-Vorschläge für Objektbeschreibung:", fg="white", bg="#202020", font=("Arial", 10, "bold"), ) label.pack(pady=(10, 5)) entry = tk.Entry(root, width=52, font=("Arial", 11)) entry.pack(pady=(0, 10)) listbox = tk.Listbox(root, width=52, height=9, font=("Arial", 10)) listbox.pack(pady=5) # ------------------------------------------------------------------------------ # 5. Matching + UNO-Kommunikation # ------------------------------------------------------------------------------ def update_suggestions(event=None): """Aktualisiert Vorschläge bei jeder Tasteneingabe.""" query = entry.get().strip() listbox.delete(0, tk.END) if not query: return try: matches = process.extract(query, NV_MASTER_LIST, scorer=fuzz.token_set_ratio, limit=8) for m in matches: item, score, _ = m listbox.insert(tk.END, f"{item} ({score:.1f}%)") if uno_available and desktop: try: doc = desktop.getCurrentComponent() if not doc: log("UNO: Kein aktives Dokument.") return sheet = doc.CurrentController.ActiveSheet cell = sheet.getCurrentSelection() if cell: cell.String = query except Exception as e: log(f"UNO-Update-Fehler: {e}") log(f"Suchanfrage '{query}' – {len(matches)} Vorschläge generiert.") except Exception as e: log(f"Fehler in update_suggestions(): {e}\n{traceback.format_exc()}") entry.bind("", update_suggestions) # ------------------------------------------------------------------------------ # 6. Laufzeit / Beenden # ------------------------------------------------------------------------------ log("nvWindow gestartet. Tkinter-Fenster aktiv.") print("nvWindow läuft – Details siehe nvWindow.log") try: root.mainloop() except KeyboardInterrupt: log("Manuell abgebrochen.") except Exception as e: log(f"Fehler in mainloop(): {e}\n{traceback.format_exc()}") finally: log("nvWindow beendet.")