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

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()