- 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
324 lines
10 KiB
Python
324 lines
10 KiB
Python
#!/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()
|