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 *.pdf
.venv/ .venv/
__pycache__/ __pycache__/
.pdf_to_ics_config.json

View File

@@ -42,11 +42,21 @@ Doppelklick auf start_gui.cmd
## GUI-Features ## 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 📋 **Mehrere PDFs:** Wählen Sie mehrere Dateien gleichzeitig
📁 **Ausgabe-Verzeichnis:** Wählen Sie, wo die ICS-Dateien gespeichert werden 📁 **Ausgabe-Verzeichnis:** Wählen Sie, wo die ICS-Dateien gespeichert werden
📊 **Echtzeit-Log:** Sehen Sie den Fortschritt live 📊 **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 ## Fehlerbehebung

167
gui.py
View File

@@ -8,8 +8,20 @@ import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext from tkinter import ttk, filedialog, messagebox, scrolledtext
from pathlib import Path from pathlib import Path
import threading import threading
import re
import json
from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan 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: class PDFtoICSGUI:
def __init__(self, root): def __init__(self, root):
@@ -18,13 +30,32 @@ class PDFtoICSGUI:
self.root.geometry("800x600") self.root.geometry("800x600")
self.root.minsize(700, 500) self.root.minsize(700, 500)
# Lade gespeicherte Einstellungen
self.config = self.load_config()
# Variablen # Variablen
self.pdf_files = [] 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 # UI erstellen
self.create_widgets() 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): def create_widgets(self):
"""Erstelle die UI-Komponenten""" """Erstelle die UI-Komponenten"""
@@ -113,6 +144,20 @@ class PDFtoICSGUI:
) )
clear_btn.pack(side=tk.LEFT) 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 # Ausgabe-Verzeichnis
output_frame = tk.Frame(content_frame) output_frame = tk.Frame(content_frame)
output_frame.pack(fill=tk.X, pady=(10, 10)) output_frame.pack(fill=tk.X, pady=(10, 10))
@@ -162,30 +207,89 @@ class PDFtoICSGUI:
) )
self.log_text.pack(fill=tk.BOTH, expand=True) 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 # Fortschrittsbalken
self.progress = ttk.Progressbar( self.progress = ttk.Progressbar(
content_frame, content_frame,
mode='determinate', mode='determinate',
length=300 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 def load_config(self):
self.log("Bereit. Fügen Sie PDF-Dateien hinzu um zu starten.") """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): def log(self, message):
"""Füge eine Nachricht zum Log hinzu""" """Füge eine Nachricht zum Log hinzu"""
@@ -196,9 +300,15 @@ class PDFtoICSGUI:
def add_pdf_files(self): def add_pdf_files(self):
"""Öffne Datei-Dialog zum Hinzufügen von PDFs""" """Ö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( files = filedialog.askopenfilenames(
title="PDF-Dateien auswählen", title="PDF-Dateien auswählen",
filetypes=[("PDF Dateien", "*.pdf"), ("Alle Dateien", "*.*")] filetypes=[("PDF Dateien", "*.pdf"), ("Alle Dateien", "*.*")],
initialdir=initial_dir
) )
for file in files: for file in files:
@@ -207,6 +317,8 @@ class PDFtoICSGUI:
self.pdf_listbox.insert(tk.END, Path(file).name) self.pdf_listbox.insert(tk.END, Path(file).name)
if files: 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") self.log(f"{len(files)} PDF-Datei(en) hinzugefügt")
def remove_selected_pdfs(self): def remove_selected_pdfs(self):
@@ -232,13 +344,20 @@ class PDFtoICSGUI:
def browse_output_dir(self): def browse_output_dir(self):
"""Öffne Dialog zur Auswahl des Ausgabe-Verzeichnisses""" """Ö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( directory = filedialog.askdirectory(
title="Ausgabe-Verzeichnis auswählen", title="Ausgabe-Verzeichnis auswählen",
initialdir=self.output_dir.get() initialdir=initial_dir,
mustexist=True
) )
if directory: if directory:
self.output_dir.set(directory) self.output_dir.set(directory)
self.save_config() # Sofort speichern
self.log(f"✓ Ausgabe-Verzeichnis: {directory}") self.log(f"✓ Ausgabe-Verzeichnis: {directory}")
def convert_pdfs(self): def convert_pdfs(self):
@@ -314,7 +433,13 @@ class PDFtoICSGUI:
def main(): def main():
"""Hauptfunktion""" """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) app = PDFtoICSGUI(root)
root.mainloop() root.mainloop()

View File

@@ -53,6 +53,12 @@ if ! $PYTHON_VENV -c "import pdfplumber" 2>/dev/null; then
fi fi
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 # Starte die GUI
echo "🎨 Starte GUI..." echo "🎨 Starte GUI..."
if [ -f "$PYTHON_VENV" ]; then if [ -f "$PYTHON_VENV" ]; then