#!/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()