GUI-Version mit Tkinter hinzugefügt

- gui.py: Grafische Benutzeroberfläche
- start_gui.sh/cmd: Startskripte für GUI
- GUI_README.md: Installationsanleitung für Tkinter
- Features: PDF-Verwaltung, Fortschrittsanzeige, Live-Log
This commit is contained in:
2026-02-23 11:10:57 +01:00
parent 910e5c522d
commit 13c280e929
4 changed files with 496 additions and 0 deletions

73
GUI_README.md Normal file
View File

@@ -0,0 +1,73 @@
# 🎨 GUI Installation
Die grafische Benutzeroberfläche benötigt Tkinter, das auf manchen Systemen separat installiert werden muss.
## Installation von Tkinter
### Ubuntu/Debian
```bash
sudo apt-get update
sudo apt-get install python3-tk
```
### Fedora/RHEL
```bash
sudo dnf install python3-tkinter
```
### Arch Linux
```bash
sudo pacman -S tk
```
### macOS
Tkinter ist bereits mit Python installiert - nichts zu tun! ✓
### Windows
Tkinter ist bereits mit Python installiert - nichts zu tun! ✓
## GUI starten
Nach der Tkinter-Installation:
**Linux/macOS:**
```bash
./start_gui.sh
```
**Windows:**
```
Doppelklick auf start_gui.cmd
```
## GUI-Features
**Drag & Drop-Alternative:** Klicken Sie auf "PDF hinzufügen"
📋 **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
## Fehlerbehebung
### "No module named 'tkinter'"
→ Tkinter muss installiert werden (siehe oben)
### GUI startet nicht
→ Versuchen Sie:
```bash
rm -rf .venv
./start_gui.sh
```
### Fenster erscheint nicht
→ Stellen Sie sicher, dass Sie eine grafische Oberfläche haben (kein SSH ohne X11)
## Alternative: CLI-Version
Falls Tkinter nicht installiert werden kann, nutzen Sie die CLI-Version:
```bash
./start.sh
```
Die CLI-Version funktioniert überall ohne zusätzliche Installation! 🚀

323
gui.py Normal file
View File

@@ -0,0 +1,323 @@
#!/usr/bin/env python3
"""
GUI für PDF zu ICS Konverter
Grafische Benutzeroberfläche mit Tkinter
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from pathlib import Path
import threading
from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan
class PDFtoICSGUI:
def __init__(self, root):
self.root = root
self.root.title("PDF zu ICS Konverter - Dienstplan Importer")
self.root.geometry("800x600")
self.root.minsize(700, 500)
# Variablen
self.pdf_files = []
self.output_dir = tk.StringVar(value=str(Path.cwd()))
# UI erstellen
self.create_widgets()
def create_widgets(self):
"""Erstelle die UI-Komponenten"""
# Header
header_frame = tk.Frame(self.root, bg="#2c3e50", height=80)
header_frame.pack(fill=tk.X)
header_frame.pack_propagate(False)
title_label = tk.Label(
header_frame,
text="📅 PDF zu ICS Konverter",
font=("Arial", 20, "bold"),
bg="#2c3e50",
fg="white"
)
title_label.pack(pady=20)
# Main Content Frame
content_frame = tk.Frame(self.root, padx=20, pady=20)
content_frame.pack(fill=tk.BOTH, expand=True)
# PDF-Dateien Bereich
pdf_label = tk.Label(
content_frame,
text="PDF-Dateien:",
font=("Arial", 12, "bold")
)
pdf_label.pack(anchor=tk.W, pady=(0, 5))
# Listbox mit Scrollbar für PDFs
list_frame = tk.Frame(content_frame)
list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
scrollbar = tk.Scrollbar(list_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.pdf_listbox = tk.Listbox(
list_frame,
selectmode=tk.EXTENDED,
yscrollcommand=scrollbar.set,
font=("Arial", 10)
)
self.pdf_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.pdf_listbox.yview)
# Buttons für PDF-Verwaltung
button_frame = tk.Frame(content_frame)
button_frame.pack(fill=tk.X, pady=(0, 10))
add_btn = tk.Button(
button_frame,
text=" PDF hinzufügen",
command=self.add_pdf_files,
bg="#3498db",
fg="white",
font=("Arial", 10, "bold"),
padx=20,
pady=8,
cursor="hand2"
)
add_btn.pack(side=tk.LEFT, padx=(0, 5))
remove_btn = tk.Button(
button_frame,
text=" Entfernen",
command=self.remove_selected_pdfs,
bg="#e74c3c",
fg="white",
font=("Arial", 10, "bold"),
padx=20,
pady=8,
cursor="hand2"
)
remove_btn.pack(side=tk.LEFT, padx=(0, 5))
clear_btn = tk.Button(
button_frame,
text="🗑️ Alle entfernen",
command=self.clear_all_pdfs,
bg="#95a5a6",
fg="white",
font=("Arial", 10, "bold"),
padx=20,
pady=8,
cursor="hand2"
)
clear_btn.pack(side=tk.LEFT)
# Ausgabe-Verzeichnis
output_frame = tk.Frame(content_frame)
output_frame.pack(fill=tk.X, pady=(10, 10))
output_label = tk.Label(
output_frame,
text="Ausgabe-Verzeichnis:",
font=("Arial", 10)
)
output_label.pack(side=tk.LEFT, padx=(0, 5))
output_entry = tk.Entry(
output_frame,
textvariable=self.output_dir,
font=("Arial", 10),
state="readonly"
)
output_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
browse_btn = tk.Button(
output_frame,
text="📁 Durchsuchen",
command=self.browse_output_dir,
bg="#16a085",
fg="white",
font=("Arial", 10),
padx=15,
pady=5,
cursor="hand2"
)
browse_btn.pack(side=tk.LEFT)
# Log-Bereich
log_label = tk.Label(
content_frame,
text="Status:",
font=("Arial", 10, "bold")
)
log_label.pack(anchor=tk.W, pady=(10, 5))
self.log_text = scrolledtext.ScrolledText(
content_frame,
height=8,
font=("Consolas", 9),
bg="#f8f9fa",
state=tk.DISABLED
)
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))
# Initial log message
self.log("Bereit. Fügen Sie PDF-Dateien hinzu um zu starten.")
def log(self, message):
"""Füge eine Nachricht zum Log hinzu"""
self.log_text.config(state=tk.NORMAL)
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
self.log_text.config(state=tk.DISABLED)
def add_pdf_files(self):
"""Öffne Datei-Dialog zum Hinzufügen von PDFs"""
files = filedialog.askopenfilenames(
title="PDF-Dateien auswählen",
filetypes=[("PDF Dateien", "*.pdf"), ("Alle Dateien", "*.*")]
)
for file in files:
if file not in self.pdf_files:
self.pdf_files.append(file)
self.pdf_listbox.insert(tk.END, Path(file).name)
if files:
self.log(f"{len(files)} PDF-Datei(en) hinzugefügt")
def remove_selected_pdfs(self):
"""Entferne ausgewählte PDFs aus der Liste"""
selected = self.pdf_listbox.curselection()
# Rückwärts durchlaufen, um Indexprobleme zu vermeiden
for index in reversed(selected):
self.pdf_listbox.delete(index)
del self.pdf_files[index]
if selected:
self.log(f"{len(selected)} PDF-Datei(en) entfernt")
def clear_all_pdfs(self):
"""Entferne alle PDFs aus der Liste"""
count = len(self.pdf_files)
self.pdf_listbox.delete(0, tk.END)
self.pdf_files.clear()
if count > 0:
self.log(f"✓ Alle {count} PDF-Datei(en) entfernt")
def browse_output_dir(self):
"""Öffne Dialog zur Auswahl des Ausgabe-Verzeichnisses"""
directory = filedialog.askdirectory(
title="Ausgabe-Verzeichnis auswählen",
initialdir=self.output_dir.get()
)
if directory:
self.output_dir.set(directory)
self.log(f"✓ Ausgabe-Verzeichnis: {directory}")
def convert_pdfs(self):
"""Konvertiere alle PDFs zu ICS"""
if not self.pdf_files:
messagebox.showwarning(
"Keine PDFs",
"Bitte fügen Sie mindestens eine PDF-Datei hinzu."
)
return
# Starte Konvertierung in separatem Thread
thread = threading.Thread(target=self._convert_worker, daemon=True)
thread.start()
def _convert_worker(self):
"""Worker-Thread für Konvertierung"""
self.progress['maximum'] = len(self.pdf_files)
self.progress['value'] = 0
output_dir = Path(self.output_dir.get())
success_count = 0
self.log("\n" + "="*50)
self.log("🔄 Starte Konvertierung...")
self.log("="*50)
for i, pdf_path in enumerate(self.pdf_files, 1):
try:
self.log(f"\n[{i}/{len(self.pdf_files)}] Verarbeite: {Path(pdf_path).name}")
# Extrahiere Daten
dienstplan = extract_dienstplan_data(pdf_path)
# Zeige Informationen
self.log(f" ├─ Name: {dienstplan['vorname']} {dienstplan['name']}")
self.log(f" ├─ Personalnummer: {dienstplan['personalnummer']}")
self.log(f" ├─ Betriebshof: {dienstplan['betriebshof']}")
self.log(f" └─ Events gefunden: {len(dienstplan['events'])}")
if not dienstplan['events']:
self.log(" ⚠️ Warnung: Keine Events gefunden!")
continue
# Erstelle ICS-Datei
ics_filename = Path(pdf_path).stem + '.ics'
ics_path = output_dir / ics_filename
create_ics_from_dienstplan(dienstplan, str(ics_path))
self.log(f" ✓ ICS erstellt: {ics_filename}")
success_count += 1
except Exception as e:
self.log(f" ✗ Fehler: {str(e)}")
finally:
self.progress['value'] = i
# Zusammenfassung
self.log("\n" + "="*50)
self.log(f"✅ Fertig! {success_count}/{len(self.pdf_files)} ICS-Dateien erstellt")
self.log("="*50 + "\n")
# Erfolgsmeldung
if success_count > 0:
self.root.after(0, lambda: messagebox.showinfo(
"Konvertierung abgeschlossen",
f"Es wurden {success_count} ICS-Datei(en) erfolgreich erstellt!\n\n"
f"Speicherort: {output_dir}"
))
def main():
"""Hauptfunktion"""
root = tk.Tk()
app = PDFtoICSGUI(root)
root.mainloop()
if __name__ == '__main__':
main()

34
start_gui.cmd Normal file
View File

@@ -0,0 +1,34 @@
@echo off
REM PDF zu ICS Konverter - GUI Startskript (Windows)
setlocal enabledelayedexpansion
REM Wechsel ins Skriptverzeichnis
cd /d "%~dp0"
REM Überprüfe, ob venv existiert
if not exist ".venv" (
echo 📦 Python-Umgebung wird eingerichtet...
python3 -m venv .venv --upgrade-deps
if errorlevel 1 (
python -m venv .venv --upgrade-deps
)
)
REM Überprüfe, ob Abhängigkeiten installiert sind
.venv\Scripts\python.exe -c "import pdfplumber" 2>nul
if errorlevel 1 (
echo 📚 Installiere Abhängigkeiten...
call .venv\Scripts\python.exe -m pip install -q pdfplumber icalendar pypdf2 pytz
echo ✓ Abhängigkeiten installiert
)
REM Starte die GUI
echo 🎨 Starte GUI...
call .venv\Scripts\pythonw.exe gui.py
if errorlevel 1 (
echo.
echo ❌ Fehler beim Starten der GUI
pause
)

66
start_gui.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
# PDF zu ICS Konverter - GUI Startskript
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"
# Finde Python-Executable
PYTHON_CMD=""
if command -v python3 &> /dev/null; then
PYTHON_CMD="python3"
elif command -v python &> /dev/null; then
PYTHON_CMD="python"
else
echo "❌ Fehler: Python nicht gefunden!"
echo "Bitte installieren Sie Python 3.6 oder höher."
exit 1
fi
echo "🐍 Nutze: $PYTHON_CMD"
# Erstelle venv wenn nicht vorhanden
if [ ! -d ".venv" ]; then
echo "📦 Python-Umgebung wird eingerichtet..."
$PYTHON_CMD -m venv .venv --upgrade-deps || {
echo "❌ venv konnte nicht erstellt werden"
exit 1
}
fi
# Nutze Python aus venv
PYTHON_VENV=".venv/bin/python"
# Überprüfe, ob Abhängigkeiten installiert sind
if ! $PYTHON_VENV -c "import pdfplumber" 2>/dev/null; then
echo "📚 Installiere Abhängigkeiten..."
if $PYTHON_VENV -m pip install -q pdfplumber icalendar pypdf2 pytz 2>/dev/null; then
echo "✓ Abhängigkeiten installiert"
else
echo "❌ Installation fehlgeschlagen"
echo "🔧 Versuche venv neu aufzubauen..."
rm -rf .venv
$PYTHON_CMD -m venv .venv --upgrade-deps || {
echo "❌ venv konnte nicht neu erstellt werden"
exit 1
}
$PYTHON_VENV -m pip install -q pdfplumber icalendar pypdf2 pytz || {
echo "❌ Abhängigkeiten konnten nicht installiert werden"
exit 1
}
echo "✓ venv neu erstellt"
fi
fi
# Starte die GUI
echo "🎨 Starte GUI..."
if [ -f "$PYTHON_VENV" ]; then
$PYTHON_VENV gui.py
else
echo "❌ Fehler: Python-Umgebung ist beschädigt"
echo "📁 Bitte löschen Sie das .venv Verzeichnis und versuchen Sie erneut:"
echo " rm -rf .venv"
echo " ./start_gui.sh"
exit 1
fi