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:
73
GUI_README.md
Normal file
73
GUI_README.md
Normal 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
323
gui.py
Normal 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
34
start_gui.cmd
Normal 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
66
start_gui.sh
Executable 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
|
||||
Reference in New Issue
Block a user