From 881fc876e8bf9b1ad617d23fdc99b7a9dc62c623 Mon Sep 17 00:00:00 2001 From: webfarben Date: Mon, 23 Feb 2026 11:58:55 +0100 Subject: [PATCH] GUI Verbesserungen: Drag & Drop, Verzeichnis-Speicherung und UI-Optimierungen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drag & Drop für PDF-Dateien hinzugefügt (mit tkinterdnd2) - Letzte Verzeichnisse werden in ~/.pdf_to_ics_config.json gespeichert - Konvertieren-Button kompakter neben 'Alle entfernen' platziert - Button umbenannt zu 'ICS Datei erstellen' - Automatische Installation von tkinterdnd2 im Startskript - .gitignore erweitert um Config-Datei --- .gitignore | 1 + GUI_README.md | 14 ++++- gui.py | 167 +++++++++++++++++++++++++++++++++++++++++++------- start_gui.sh | 6 ++ 4 files changed, 165 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 4f033f5..4a8adbf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.pdf .venv/ __pycache__/ +.pdf_to_ics_config.json diff --git a/GUI_README.md b/GUI_README.md index d8a1858..1050cbb 100644 --- a/GUI_README.md +++ b/GUI_README.md @@ -42,11 +42,21 @@ Doppelklick auf start_gui.cmd ## GUI-Features -✨ **Drag & Drop-Alternative:** Klicken Sie auf "PDF hinzufügen" +✨ **Drag & Drop:** Ziehen Sie PDF-Dateien direkt in die Liste (optional mit tkinterdnd2) 📋 **Mehrere PDFs:** Wählen Sie mehrere Dateien gleichzeitig 📁 **Ausgabe-Verzeichnis:** Wählen Sie, wo die ICS-Dateien gespeichert werden 📊 **Echtzeit-Log:** Sehen Sie den Fortschritt live -✅ **Fortschrittsbalken:** Visuelles Feedback bei der Konvertierung +✅ **Fortschrittsbalken:** Visuelles Feedback bei der Konvertierung + +### Drag & Drop aktivieren (optional) + +Für besseres Drag & Drop installieren Sie tkinterdnd2: + +```bash +.venv/bin/pip install tkinterdnd2 +``` + +Oder lassen Sie das Startskript es automatisch installieren. ## Fehlerbehebung diff --git a/gui.py b/gui.py index a23f7c3..10c010d 100644 --- a/gui.py +++ b/gui.py @@ -8,8 +8,20 @@ import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext from pathlib import Path import threading +import re +import json from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan +# Versuche tkinterdnd2 zu importieren (optional für besseres Drag & Drop) +try: + from tkinterdnd2 import DND_FILES, TkinterDnD + HAS_TKINTERDND = True +except ImportError: + HAS_TKINTERDND = False + +# Konfigurationsdatei +CONFIG_FILE = Path.home() / '.pdf_to_ics_config.json' + class PDFtoICSGUI: def __init__(self, root): @@ -18,13 +30,32 @@ class PDFtoICSGUI: self.root.geometry("800x600") self.root.minsize(700, 500) + # Lade gespeicherte Einstellungen + self.config = self.load_config() + # Variablen self.pdf_files = [] - self.output_dir = tk.StringVar(value=str(Path.cwd())) + + # Nutze letztes Ausgabeverzeichnis oder Standard + default_dir = self.config.get('last_output_dir', None) + if not default_dir or not Path(default_dir).exists(): + default_dir = Path.cwd() + if str(default_dir).split('/')[-1].startswith('.'): + default_dir = Path.home() + self.output_dir = tk.StringVar(value=str(default_dir)) + + # Letztes PDF-Verzeichnis merken + self.last_pdf_dir = self.config.get('last_pdf_dir', str(Path.home())) # UI erstellen self.create_widgets() + # Drag & Drop einrichten + self.setup_drag_and_drop() + + # Speichere Konfiguration beim Schließen + self.root.protocol("WM_DELETE_WINDOW", self.on_closing) + def create_widgets(self): """Erstelle die UI-Komponenten""" @@ -113,6 +144,20 @@ class PDFtoICSGUI: ) clear_btn.pack(side=tk.LEFT) + # Konvertieren Button (rechts in der Zeile) + self.convert_btn = tk.Button( + button_frame, + text="📄 ICS Datei erstellen", + command=self.convert_pdfs, + bg="#27ae60", + fg="white", + font=("Arial", 10, "bold"), + padx=20, + pady=8, + cursor="hand2" + ) + self.convert_btn.pack(side=tk.RIGHT) + # Ausgabe-Verzeichnis output_frame = tk.Frame(content_frame) output_frame.pack(fill=tk.X, pady=(10, 10)) @@ -162,30 +207,89 @@ class PDFtoICSGUI: ) self.log_text.pack(fill=tk.BOTH, expand=True) - # Konvertieren Button (groß und zentral) - convert_btn = tk.Button( - content_frame, - text="🔄 PDFs konvertieren", - command=self.convert_pdfs, - bg="#27ae60", - fg="white", - font=("Arial", 14, "bold"), - padx=30, - pady=15, - cursor="hand2" - ) - convert_btn.pack(pady=20) - # Fortschrittsbalken self.progress = ttk.Progressbar( content_frame, mode='determinate', length=300 ) - self.progress.pack(pady=(0, 10)) + drag_info = " (Drag & Drop unterstützt)" if HAS_TKINTERDND else " (Tipp: Installiere tkinterdnd2 für Drag & Drop)" + self.log(f"Bereit. Fügen Sie PDF-Dateien hinzu um zu starten.{drag_info}") - # Initial log message - self.log("Bereit. Fügen Sie PDF-Dateien hinzu um zu starten.") + def load_config(self): + """Lade gespeicherte Konfiguration""" + try: + if CONFIG_FILE.exists(): + with open(CONFIG_FILE, 'r') as f: + return json.load(f) + except Exception as e: + print(f"Warnung: Konfiguration konnte nicht geladen werden: {e}") + return {} + + def save_config(self): + """Speichere Konfiguration""" + try: + config = { + 'last_output_dir': self.output_dir.get(), + 'last_pdf_dir': self.last_pdf_dir + } + with open(CONFIG_FILE, 'w') as f: + json.dump(config, f, indent=2) + except Exception as e: + print(f"Warnung: Konfiguration konnte nicht gespeichert werden: {e}") + + def on_closing(self): + """Handle für Fenster schließen""" + self.save_config() + self.root.destroy() + + def setup_drag_and_drop(self): + """Richte Drag & Drop ein""" + if HAS_TKINTERDND: + # Verwende tkinterdnd2 wenn verfügbar + self.pdf_listbox.drop_target_register(DND_FILES) + self.pdf_listbox.dnd_bind('<>', self.drop_files) + else: + # Fallback: Native Tkinter Drag & Drop (funktioniert auf Unix) + try: + self.pdf_listbox.drop_target_register('DND_Files') + self.pdf_listbox.dnd_bind('<>', self.drop_files) + except: + # Wenn auch das nicht funktioniert, zeige Hilfstext + pass + + def drop_files(self, event): + """Handle für Drag & Drop Events""" + files = [] + + if HAS_TKINTERDND: + # Parse tkinterdnd2 format + files_str = event.data + # Entferne geschweifte Klammern und splitte + files_str = files_str.strip('{}') + files = re.findall(r'[^\s{}]+(?:\s+[^\s{}]+)*', files_str) + else: + # Fallback parsing + if hasattr(event, 'data'): + files_str = event.data.strip('{}') + files = [f.strip() for f in files_str.split()] + + # Füge nur PDF-Dateien hinzu + pdf_count = 0 + for file_path in files: + file_path = file_path.strip() + if file_path.lower().endswith('.pdf'): + if file_path not in self.pdf_files: + self.pdf_files.append(file_path) + self.pdf_listbox.insert(tk.END, Path(file_path).name) + pdf_count += 1 + + if pdf_count > 0: + self.log(f"✓ {pdf_count} PDF-Datei(en) per Drag & Drop hinzugefügt") + elif files: + self.log("⚠ Nur PDF-Dateien können hinzugefügt werden") + + return 'break' def log(self, message): """Füge eine Nachricht zum Log hinzu""" @@ -196,9 +300,15 @@ class PDFtoICSGUI: def add_pdf_files(self): """Öffne Datei-Dialog zum Hinzufügen von PDFs""" + # Starte im letzten PDF-Verzeichnis + initial_dir = self.last_pdf_dir + if initial_dir.startswith('.') or not Path(initial_dir).exists(): + initial_dir = str(Path.home()) + files = filedialog.askopenfilenames( title="PDF-Dateien auswählen", - filetypes=[("PDF Dateien", "*.pdf"), ("Alle Dateien", "*.*")] + filetypes=[("PDF Dateien", "*.pdf"), ("Alle Dateien", "*.*")], + initialdir=initial_dir ) for file in files: @@ -207,6 +317,8 @@ class PDFtoICSGUI: self.pdf_listbox.insert(tk.END, Path(file).name) if files: + # Merke Verzeichnis der ersten ausgewählten Datei + self.last_pdf_dir = str(Path(files[0]).parent) self.log(f"✓ {len(files)} PDF-Datei(en) hinzugefügt") def remove_selected_pdfs(self): @@ -232,13 +344,20 @@ class PDFtoICSGUI: def browse_output_dir(self): """Öffne Dialog zur Auswahl des Ausgabe-Verzeichnisses""" + # Verhindere Start in versteckten Verzeichnissen + initial_dir = self.output_dir.get() + if initial_dir.startswith('.') or '/.venv' in initial_dir or '/__pycache__' in initial_dir: + initial_dir = str(Path.home()) + directory = filedialog.askdirectory( title="Ausgabe-Verzeichnis auswählen", - initialdir=self.output_dir.get() + initialdir=initial_dir, + mustexist=True ) if directory: self.output_dir.set(directory) + self.save_config() # Sofort speichern self.log(f"✓ Ausgabe-Verzeichnis: {directory}") def convert_pdfs(self): @@ -314,7 +433,13 @@ class PDFtoICSGUI: def main(): """Hauptfunktion""" - root = tk.Tk() + if HAS_TKINTERDND: + # Verwende TkinterDnD root für besseres Drag & Drop + root = TkinterDnD.Tk() + else: + # Standard Tkinter + root = tk.Tk() + app = PDFtoICSGUI(root) root.mainloop() diff --git a/start_gui.sh b/start_gui.sh index 09585ec..3757096 100755 --- a/start_gui.sh +++ b/start_gui.sh @@ -53,6 +53,12 @@ if ! $PYTHON_VENV -c "import pdfplumber" 2>/dev/null; then fi fi +# Versuche tkinterdnd2 zu installieren (optional für Drag & Drop) +if ! $PYTHON_VENV -c "import tkinterdnd2" 2>/dev/null; then + echo "💡 Installiere tkinterdnd2 für Drag & Drop (optional)..." + $PYTHON_VENV -m pip install -q tkinterdnd2 2>/dev/null && echo "✓ Drag & Drop aktiviert" || echo "ℹ️ Drag & Drop nicht verfügbar (kein Problem)" +fi + # Starte die GUI echo "🎨 Starte GUI..." if [ -f "$PYTHON_VENV" ]; then