GND_Skript_Test/nvWindow.py

236 lines
8.0 KiB
Python
Raw Permalink 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 -*-
"""
===============================================================================
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("<KeyRelease>", 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.")