GUI Verbesserungen: Drag & Drop, Verzeichnis-Speicherung und UI-Optimierungen

- 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
This commit is contained in:
2026-02-23 11:58:55 +01:00
parent 13c280e929
commit 881fc876e8
4 changed files with 165 additions and 23 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
*.pdf
.venv/
__pycache__/
.pdf_to_ics_config.json

View File

@@ -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

167
gui.py
View File

@@ -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('<<Drop>>', 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('<<Drop>>', 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()

View File

@@ -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