diff --git a/GUI_README.md b/GUI_README.md index 1050cbb..b68b364 100644 --- a/GUI_README.md +++ b/GUI_README.md @@ -1,35 +1,9 @@ -# 🎨 GUI Installation +# 🎨 GUI Installation (wxPython) -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! ✓ +Die grafische Benutzeroberfläche nutzt **wxPython** für ein natives Look & Feel auf Linux, macOS und Windows. ## GUI starten -Nach der Tkinter-Installation: - **Linux/macOS:** ```bash ./start_gui.sh @@ -40,44 +14,41 @@ Nach der Tkinter-Installation: Doppelklick auf start_gui.cmd ``` +Beim ersten Start werden `.venv`, Kern-Abhängigkeiten und `wxPython` automatisch installiert. + ## GUI-Features -✨ **Drag & Drop:** Ziehen Sie PDF-Dateien direkt in die Liste (optional mit tkinterdnd2) +✨ **Native Oberfläche:** Optisch passend zum Betriebssystem 📋 **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 - -### Drag & Drop aktivieren (optional) - -Für besseres Drag & Drop installieren Sie tkinterdnd2: - -```bash -.venv/bin/pip install tkinterdnd2 -``` - -Oder lassen Sie das Startskript es automatisch installieren. +🖱️ **Drag & Drop:** Direkt in die PDF-Liste ziehen ## Fehlerbehebung -### "No module named 'tkinter'" -→ Tkinter muss installiert werden (siehe oben) +### "No module named 'wx'" +→ `wxPython` konnte nicht installiert werden. + +Unter Linux Mint/Ubuntu helfen häufig: +```bash +sudo apt-get update +sudo apt-get install -y build-essential python3-dev libgtk-3-dev libglib2.0-dev libjpeg-dev libtiff-dev libpng-dev +rm -rf .venv +./start_gui.sh +``` ### 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) +→ Stellen Sie sicher, dass eine grafische Sitzung aktiv ist (kein reines SSH ohne X11/Wayland-Forwarding). ## Alternative: CLI-Version -Falls Tkinter nicht installiert werden kann, nutzen Sie die CLI-Version: +Falls keine GUI möglich ist: ```bash ./start.sh ``` - -Die CLI-Version funktioniert überall ohne zusätzliche Installation! 🚀 diff --git a/INSTALL.md b/INSTALL.md index fabe6e5..f9e6872 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -14,7 +14,7 @@ Das war's! Die Anwendung erscheint nun in Ihrem Anwendungsmenü unter "PDF zu IC ## 📋 Was macht das Installations-Script? 1. ✅ **Prüft Python-Installation** (Python 3.6+) -2. ✅ **Installiert Tkinter** falls nötig (mit sudo-Berechtigung) +2. ✅ **Installiert wxPython** in der virtuellen Umgebung 3. ✅ **Erstellt Installationsverzeichnis** in `~/.local/share/pdf-to-ics` 4. ✅ **Kopiert alle Dateien** ins Installationsverzeichnis 5. ✅ **Erstellt Python Virtual Environment** mit allen Abhängigkeiten @@ -43,14 +43,14 @@ pdf-to-ics ## 🔧 Systemanforderungen ### Unterstützte Distributionen: -- ✅ Ubuntu / Debian (automatische Tkinter-Installation) -- ✅ Fedora / RHEL (automatische Tkinter-Installation) -- ✅ Arch Linux (automatische Tkinter-Installation) -- ✅ Andere Distributionen (manuelle Tkinter-Installation erforderlich) +- ✅ Ubuntu / Debian / Linux Mint +- ✅ Fedora / RHEL +- ✅ Arch Linux +- ✅ Andere Distributionen (ggf. zusätzliche Build-Abhängigkeiten nötig) ### Voraussetzungen: - Python 3.6 oder höher -- `sudo`-Berechtigung (für Tkinter-Installation) +- Internetzugang für `pip install wxPython` - Etwa 50 MB Festplattenspeicher ## 📁 Installations-Pfade @@ -76,21 +76,12 @@ Das Deinstallations-Script entfernt: ## ⚠️ Fehlerbehebung -### "Tkinter ist nicht installiert" +### "wxPython konnte nicht installiert werden" -**Ubuntu/Debian:** +**Ubuntu/Debian/Linux Mint:** ```bash -sudo apt-get install python3-tk -``` - -**Fedora:** -```bash -sudo dnf install python3-tkinter -``` - -**Arch Linux:** -```bash -sudo pacman -S tk +sudo apt-get update +sudo apt-get install -y build-essential python3-dev libgtk-3-dev libglib2.0-dev libjpeg-dev libtiff-dev libpng-dev ``` ### "pdf-to-ics: Befehl nicht gefunden" @@ -115,7 +106,7 @@ source ~/.bashrc ```bat py -3 -m venv .venv --upgrade-deps .\.venv\Scripts\python.exe -m pip install -q pdfplumber icalendar pypdf2 pytz packaging -.\.venv\Scripts\pythonw.exe gui.py +.\.venv\Scripts\pythonw.exe gui_wxpython.py ``` ### Anwendung erscheint nicht im Menü diff --git a/QUICKSTART.md b/QUICKSTART.md index fbb06bc..f7928df 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -23,9 +23,9 @@ Kopieren Sie Ihre Dienstplan-PDF-Dateien in dieses Verzeichnis: /home/sebastian/Dokumente/ICS-Import/ ``` -### 3. Konvertieren und Importieren +## 3. Konvertieren und Importieren -Im Menü wählen Sie Option "1. PDF(s) konvertieren" und die ICS-Dateien werden automatisch erstellt. +In der GUI auf "ICS Datei erstellen" klicken. Die ICS-Dateien werden dann automatisch erstellt. --- diff --git a/README.md b/README.md index b917025..3bb2698 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Benutzerfreundliche grafische Oberfläche mit Drag & Drop Support. - 📊 Live-Log und Fortschrittsanzeige - 💾 Merkt sich letzte Verzeichnisse -**Voraussetzung:** Tkinter muss installiert sein (siehe [GUI_README.md](GUI_README.md)) +**Voraussetzung:** wxPython muss installiert sein (wird automatisch versucht; siehe [WXPYTHON_README.md](WXPYTHON_README.md)) ### 2. **CLI-Version** (Kommandozeile) Textbasiertes Menü für die Kommandozeile. @@ -57,7 +57,7 @@ Beim ersten Start wird automatisch eine virtuelle Umgebung (`.venv`) erstellt un ```bat py -3 -m venv .venv --upgrade-deps -.\.venv\Scripts\python.exe gui.py +.\.venv\Scripts\python.exe gui_wxpython.py ``` ### Schnellstart (Empfohlen) @@ -66,40 +66,33 @@ py -3 -m venv .venv --upgrade-deps ```bash ./start_gui.sh ``` -Siehe [GUI_README.md](GUI_README.md) für Tkinter-Installation. +Siehe [WXPYTHON_README.md](WXPYTHON_README.md) für wxPython-Hinweise. **Für CLI-Version:** ```bash ./start.sh -```GUI-Version (Empfohlen) +``` + +### GUI-Version (Empfohlen) 1. Starten Sie die GUI: - ```bash - ./start_gui.sh - ``` + ```bash + ./start_gui.sh + ``` +2. Fügen Sie PDF-Dateien hinzu (Button oder Drag & Drop). +3. Optional: Aktivieren Sie Filter wie „Ruhetage ausschließen“ und „Urlaub ausschließen (060, 0060)“. +4. Klicken Sie auf „📄 ICS Datei erstellen“. -2. Fügen Sie PDF-Dateien hinzu: - - Klicken Sie auf "➕ PDF hinzufügen", oder - - Ziehen Sie PDF-Dateien in die Liste (Drag & Drop) +Die GUI merkt sich Ihre letzten Verzeichnisse und Exportoptionen. -**Interaktives Menü:** +### CLI-Version + +Starten Sie das interaktive Menü: ```bash ./start.sh ``` -Dann wählen Sie im Menü die gewünschte Option. - -### Erweiterte Nutzung (Python-Modul)abe-Verzeichnis (optional) - -4. Klicken Sie auf "📄 ICS Datei erstellen" - -Die GUI merkt sich Ihre letzten Verzeichnisse für schnelleren Zugriff! - -### CLI-Version - -**Schnellstart:** - Beide Skripte erstellen automatisch eine Python Virtual Environment und installieren alle benötigten Abhängigkeiten. ### Manuelle Installation @@ -141,6 +134,9 @@ python3 pdf_to_ics.py --input ./pdfs --output ./ics_dateien # Ruhetage ausschließen python3 pdf_to_ics.py --exclude-rest +# Urlaub (060) ausschließen +python3 pdf_to_ics.py --exclude-vacation + # Einzelne PDF-Datei konvertieren python3 pdf_to_ics.py /pfad/zur/datei.pdf @@ -158,6 +154,7 @@ python3 pdf_to_ics.py --help | `--input DIR` | `-i` | Eingabe-Verzeichnis mit PDF-Dateien (Standard: aktuelles Verzeichnis) | | `--output DIR` | `-o` | Ausgabe-Verzeichnis für ICS-Dateien (Standard: Eingabe-Verzeichnis) | | `--exclude-rest` | `-e` | Ruhetage ausschließen (Ruhe, R56, R36, vRWF48, RWE, vR48) | +| `--exclude-vacation` | `-u` | Urlaub ausschließen (060, 0060) | | `--verbose` | `-v` | Detaillierte Ausgabe anzeigen | | `--help` | `-h` | Hilfe anzeigen | @@ -233,18 +230,18 @@ Die ICS-Datei enthält ein Event für jeden Arbeitstag mit: ``` ICS-Import/ ├── pdf_to_ics.py # Core-Konvertierungslogik -├── gui.py # GUI-Version (Tkinter) +├── gui_wxpython.py # GUI-Version (wxPython) ├── menu.py # CLI-Menü ├── start_gui.sh/cmd # GUI-Startskripte ├── start.sh/cmd # CLI-Startskripte ├── README.md # Diese Datei -└── GUI_README.md # GUI-spezifische Dokumentation +└── WXPYTHON_README.md # GUI-spezifische Dokumentation ``` ### Technische Spezifikationen - **Abhängigkeiten**: pdfplumber, icalendar, pytz, pypdf2, packaging -- **Optional für GUI**: tkinter (Python-Standard), tkinterdnd2 (Drag & Drop) +- **Optional für GUI**: wxPython (native Oberfläche) - **Python-Version**: 3.6+ - **Format**: iCalendar 2.0 (RFC 5545) - **Konfiguration**: `~/.pdf_to_ics_config.json` (GUI-Einstellungen) @@ -257,6 +254,6 @@ Dieses Tool ist zur privaten Verwendung gedacht. ## 📚 Weitere Dokumentation -- **[GUI_README.md](GUI_README.md)** - Ausführliche GUI-Dokumentation und Tkinter-Installation +- **[WXPYTHON_README.md](WXPYTHON_README.md)** - Ausführliche GUI-Dokumentation und wxPython-Hinweise - **[QUICKSTART.md](QUICKSTART.md)** - Schnellanleitung für den Import in verschiedene Kalender - **[ZUSAMMENFASSUNG.md](ZUSAMMENFASSUNG.md)** - Projekt-Übersicht und technische Details diff --git a/WEITERGABE.md b/WEITERGABE.md index fceeea2..e51c896 100644 --- a/WEITERGABE.md +++ b/WEITERGABE.md @@ -74,9 +74,10 @@ sudo apt install python3 python3-pip python3-venv ### Die Anwendung startet nicht -Prüfen Sie, ob Tkinter installiert ist: +Prüfen Sie, ob wxPython-Build-Abhängigkeiten installiert sind: ```bash -sudo apt install python3-tk +sudo apt-get update +sudo apt-get install -y build-essential python3-dev libgtk-3-dev libglib2.0-dev libjpeg-dev libtiff-dev libpng-dev ``` ## 📞 Weitere Hilfe diff --git a/WXPYTHON_README.md b/WXPYTHON_README.md index 10a1de5..910b044 100644 --- a/WXPYTHON_README.md +++ b/WXPYTHON_README.md @@ -6,15 +6,15 @@ Native GUI-Lösung mit **wxPython** - funktioniert zuverlässig auf macOS 13.6, Nach den Problemen mit BeeWare/Toga (macOS 13.7+ erforderlich) ist wxPython die perfekte Lösung: -| Feature | Tkinter | BeeWare/Toga | **wxPython** | -|---------|---------|--------------|--------------| -| Native auf macOS | ❌ | ✅ (nur 13.7+) | ✅ **Alle Versionen** | -| Native auf Windows | ❌ | ✅ | ✅ | -| Native auf Linux | ❌ | ✅ | ✅ | -| Stabilität | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | -| Look & Feel | Blechig | Modern | **Perfekt nativ** | -| macOS 13.6 Support | ✅ | ❌ | ✅ | -| Installation | Einfach | Kompliziert | Einfach | +| Feature | BeeWare/Toga | **wxPython** | +|---------|--------------|--------------| +| Native auf macOS | ✅ (nur 13.7+) | ✅ **Alle Versionen** | +| Native auf Windows | ✅ | ✅ | +| Native auf Linux | ✅ | ✅ | +| Stabilität | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | +| Look & Feel | Modern | **Perfekt nativ** | +| macOS 13.6 Support | ❌ | ✅ | +| Installation | Kompliziert | Einfach | --- @@ -58,6 +58,7 @@ Die App startet sofort mit **nativen macOS-Widgets**! 🎉 3. **Exportoptionen** - Checkbox für "Ruhetage ausschließen" + - Checkbox für "Urlaub ausschließen (060, 0060)" - Speichert Einstellung persistent 4. **Konvertierung** @@ -100,6 +101,7 @@ Die App startet sofort mit **nativen macOS-Widgets**! 🎉 │ [/Users/sebastian/Documents ] 📁 │ ← Native TextCtrl ├─────────────────────────────────────┤ │ ☐ Ruhetage ausschließen │ ← Native CheckBox +│ ☐ Urlaub ausschließen (060) │ ← Native CheckBox ├─────────────────────────────────────┤ │ Status: │ │ ┌─────────────────────────────────┐ │ @@ -115,24 +117,16 @@ Die App startet sofort mit **nativen macOS-Widgets**! 🎉 --- -## 🔧 Unterschiede zu Tkinter +## 🔧 wxPython APIs im Überblick ### 1. Event Handling ```python -# Tkinter -btn = tk.Button(text="Klick", command=self.on_click) - -# wxPython btn = wx.Button(panel, label="Klick") btn.Bind(wx.EVT_BUTTON, self.on_click) ``` ### 2. Layout ```python -# Tkinter -frame.pack(fill=tk.BOTH, expand=True) - -# wxPython sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(widget, 1, wx.EXPAND) panel.SetSizer(sizer) @@ -140,19 +134,11 @@ panel.SetSizer(sizer) ### 3. Threading + UI ```python -# Tkinter -self.log_text.insert(tk.END, "Message\n") - -# wxPython wx.CallAfter(self.log_text.AppendText, "Message\n") ``` ### 4. Dialog ```python -# Tkinter -filedialog.askopenfilenames(...) - -# wxPython with wx.FileDialog(...) as dialog: if dialog.ShowModal() == wx.ID_CANCEL: return @@ -177,7 +163,7 @@ self.pdf_listbox = wx.ListBox(panel, style=wx.LB_EXTENDED) ``` ### ✅ Bessere Performance -- Schnellere Rendering als Tkinter +- Schnelle native Darstellung - Native Controls = weniger CPU ### ✅ Moderne Features @@ -211,6 +197,7 @@ python3 gui_wxpython.py - [ ] "Alle entfernen" funktioniert - [ ] "Durchsuchen" für Ausgabe-Verzeichnis funktioniert - [ ] Checkbox "Ruhetage" funktioniert +- [ ] Checkbox "Urlaub ausschließen (060)" funktioniert - [ ] "ICS Datei erstellen" startet Konvertierung - [ ] Log zeigt Status in Echtzeit - [ ] Nach Konvertierung: Erfolgs-Dialog @@ -220,7 +207,7 @@ python3 gui_wxpython.py --- -## 📈 Migration von Tkinter +## 📈 Migration auf wxPython ### Was blieb gleich: - Threading-Logik @@ -236,25 +223,22 @@ python3 gui_wxpython.py - ✅ Native Dialoge (About, File, Directory) - ✅ Thread-sicheres UI-Update mit `wx.CallAfter` +### Status +- ✅ GUI-Start erfolgt über `start_gui.sh` / `start_gui.cmd` +- ✅ GUI-Einstiegspunkt ist `gui_wxpython.py` + --- ## 🚀 Nächste Schritte (optional) -### 1. Ersetzt Tkinter vollständig -Wenn wxPython gut funktioniert, können wir: -```bash -mv gui.py gui_tkinter_old.py -mv gui_wxpython.py gui.py -``` - -### 2. Packaging mit PyInstaller +### 1. Packaging mit PyInstaller ```bash pip install pyinstaller pyinstaller --onefile --windowed gui_wxpython.py ``` → Erstellt `.app` Bundle für macOS! -### 3. Weitere Features +### 2. Weitere Features - Icon hinzufügen - Statusbar mit Progress - Toolbar mit Icons @@ -263,19 +247,18 @@ pyinstaller --onefile --windowed gui_wxpython.py --- -## 🆚 Vergleich: Tkinter vs wxPython +## 🆚 Plattformstatus -| Kriterium | Tkinter | wxPython | -|-----------|---------|----------| -| **Look auf macOS** | ❌ Alt/blechig | ✅ **Perfekt nativ** | -| **Menüleiste** | ⚠️ Popup-Menü | ✅ **Native MenuBar** | -| **File Dialoge** | ⚠️ Ok | ✅ **Perfekt nativ** | -| **Thread-Safety** | ⚠️ Kompliziert | ✅ **wx.CallAfter** | -| **Installation** | ✅ Built-in | ⚠️ Pip install | -| **Bundle-Größe** | ✅ Klein | ⚠️ Größer (~20MB) | -| **Entwicklungszeit** | ✅ Schnell | ⚠️ Etwas länger | -| **macOS 13.6 Support** | ✅ Ja | ✅ **Ja!** | -| **Dark Mode** | ❌ | ✅ **Automatisch** | +| Kriterium | wxPython | +|-----------|----------| +| **Look auf macOS** | ✅ **Perfekt nativ** | +| **Menüleiste** | ✅ **Native MenuBar** | +| **File Dialoge** | ✅ **Perfekt nativ** | +| **Thread-Safety** | ✅ **wx.CallAfter** | +| **Installation** | ⚠️ Pip install | +| **Bundle-Größe** | ⚠️ Größer (~20MB) | +| **macOS 13.6 Support** | ✅ **Ja!** | +| **Dark Mode** | ✅ **Automatisch** | --- @@ -296,7 +279,7 @@ pyinstaller --onefile --windowed gui_wxpython.py 2. ✅ Perfekter nativer Look & Feel 3. ✅ Keine Versionskonflikte 4. ✅ Stabil und production-ready -5. ✅ Einfache Migration von Tkinter +5. ✅ Einheitlicher GUI-Stack mit wxPython 6. ✅ Alle Features vollständig implementiert **Status:** 🎉 **PRODUCTION READY!** diff --git a/ZUSAMMENFASSUNG.md b/ZUSAMMENFASSUNG.md index c0897b4..7b2be91 100644 --- a/ZUSAMMENFASSUNG.md +++ b/ZUSAMMENFASSUNG.md @@ -162,6 +162,22 @@ Jedes Event in der ICS-Datei: --- +## 📝 Changelog (März 2026) + +### v1.2.0 + +- GUI-Technik vollständig auf **wxPython** umgestellt +- Standard-Startpfade auf `gui_wxpython.py` angepasst (`start_gui.sh`, `start_gui.cmd`, `install.sh`) +- Linux-Hinweise für wxPython-Build-Abhängigkeiten ergänzt +- Alte Tkinter-GUI-Datei `gui.py` entfernt +- Neue Exportoption ergänzt: **Urlaub ausschließen (060/0060)** + - GUI: zusätzliche Checkbox + - CLI: `--exclude-vacation` / `-u` + - Core-Logik: Urlaubseinträge werden optional nicht in ICS exportiert +- Dokumentation konsolidiert und auf wxPython-only aktualisiert + +--- + ## 📞 Support Für detaillierte Informationen: @@ -175,5 +191,5 @@ Für detaillierte Informationen: Ihr System ist bereit. Viel Erfolg mit der Dienstplan-Verwaltung! 📅✨ -**Letzte Änderung:** 23. Februar 2026 +**Letzte Änderung:** 2. März 2026 **Status:** ✅ Einsatzbereit diff --git a/gui.py b/gui.py deleted file mode 100644 index 66d6d63..0000000 --- a/gui.py +++ /dev/null @@ -1,739 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -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 -import re -import json -import webbrowser -from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan -from update_checker import check_for_updates, get_current_version - -# Versuche tkinterdnd2 zu importieren (optional für besseres Drag & Drop) -try: - from tkinterdnd2 import DND_FILES, TkinterDnD - HAS_TKINTERDND = True -except ImportError: - HAS_TKINTERDND = False - -# Konfigurationsdatei -CONFIG_FILE = Path.home() / '.pdf_to_ics_config.json' - - -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) - - # Lade gespeicherte Einstellungen - self.config = self.load_config() - - # Variablen - self.pdf_files = [] - - # Erstelle Menüleiste - self.create_menu() - - # Nutze letztes Ausgabeverzeichnis oder Standard - default_dir = self.config.get('last_output_dir', None) - if not default_dir or not Path(default_dir).exists(): - default_dir = Path.cwd() - if str(default_dir).split('/')[-1].startswith('.'): - default_dir = Path.home() - self.output_dir = tk.StringVar(value=str(default_dir)) - - # Letztes PDF-Verzeichnis merken - self.last_pdf_dir = self.config.get('last_pdf_dir', str(Path.home())) - - # Exportoptionen - self.exclude_rest = tk.BooleanVar(value=self.config.get('exclude_rest', False)) - - # UI erstellen - self.create_widgets() - - # Drag & Drop einrichten - self.setup_drag_and_drop() - - # Update-Prüfung im Hintergrund starten - update_thread = threading.Thread(target=self.check_for_updates_background, daemon=True) - update_thread.start() - - # Speichere Konfiguration beim Schließen - self.root.protocol("WM_DELETE_WINDOW", self.on_closing) - - 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) - - # Konvertieren Button (rechts in der Zeile) - self.convert_btn = tk.Button( - button_frame, - text="📄 ICS Datei erstellen", - command=self.convert_pdfs, - bg="#27ae60", - fg="white", - font=("Arial", 10, "bold"), - padx=20, - pady=8, - cursor="hand2" - ) - self.convert_btn.pack(side=tk.RIGHT) - - # 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) - - # Exportoptionen - options_frame = tk.Frame(content_frame) - options_frame.pack(fill=tk.X, pady=(10, 5)) - - exclude_rest_check = tk.Checkbutton( - options_frame, - text="🧘 Ruhetage ausschließen - (Ruhe, R56, R36, vRWF48, RWE, vR48)", - variable=self.exclude_rest, - font=("Arial", 10) - ) - exclude_rest_check.pack(anchor=tk.W) - - # 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) - - # Fortschrittsbalken - self.progress = ttk.Progressbar( - content_frame, - mode='determinate', - length=300 - ) - drag_info = " (Drag & Drop unterstützt)" if HAS_TKINTERDND else " (Tipp: Installiere tkinterdnd2 für Drag & Drop)" - self.log(f"Bereit. Fügen Sie PDF-Dateien hinzu um zu starten.{drag_info}") - - def load_config(self): - """Lade gespeicherte Konfiguration""" - try: - if CONFIG_FILE.exists(): - with open(CONFIG_FILE, 'r') as f: - return json.load(f) - except Exception as e: - print(f"Warnung: Konfiguration konnte nicht geladen werden: {e}") - return {} - - def create_menu(self): - """Erstelle die Menüleiste""" - menubar = tk.Menu(self.root) - self.root.config(menu=menubar) - - # Hilfe-Menü - help_menu = tk.Menu(menubar, tearoff=0) - menubar.add_cascade(label="Hilfe", menu=help_menu) - help_menu.add_command(label="PDF-Export auf Android (iPD)", command=self.show_android_export_guide) - help_menu.add_separator() - help_menu.add_command(label="Über dieses Programm", command=self.show_about_dialog) - help_menu.add_separator() - help_menu.add_command(label="Beenden", command=self.on_closing) - - def show_android_export_guide(self): - """Zeige Anleitung für PDF-Export aus Android App (iPD)""" - guide_window = tk.Toplevel(self.root) - guide_window.title("PDF-Export auf Android (iPD)") - guide_window.geometry("550x550") - guide_window.resizable(False, False) - - # Zentriere das Fenster - guide_window.transient(self.root) - guide_window.grab_set() - - # Header - header = tk.Label( - guide_window, - text="PDF-Export aus iPD", - font=("Arial", 16, "bold"), - bg="#2c3e50", - fg="white", - pady=15 - ) - header.pack(fill=tk.X) - - # Content Frame - content = tk.Frame(guide_window, padx=20, pady=20) - content.pack(fill=tk.BOTH, expand=True) - - # Anleitung-Text - guide_text = tk.Text( - content, - height=20, - font=("Courier", 9), - fg="#34495e", - wrap=tk.WORD, - relief=tk.FLAT, - bg="#f8f9fa" - ) - - guide_content = """1. Öffne die iPD App auf deinem Android-Gerät - -2. Öffne einen Dienstplan - -3. Wähle den gewünschten Monat aus - -4. Tippe auf das PDF-Symbol - (rechts oben, links neben dem 3-Punkte-Menü) - -5. Tippe auf "Datei herunterladen" - (rechts oben, neben Drucker-Button) - -6. Wähle "Im Arbeitsprofil speichern" - -7. Sende die PDF-Datei als E-Mail-Anhang - an deine private E-Mailadresse - -8. Transferiere die PDF-Datei auf deinen Computer - -9. Öffne diese Anwendung und füge die PDF ein - -10. Klicke "ICS Datei erstellen" - -11. Importiere die ICS-Datei in deinen Kalender - -✓ Fertig!""" - - guide_text.insert(tk.END, guide_content) - guide_text.config(state=tk.DISABLED) - guide_text.pack(fill=tk.BOTH, expand=True) - - # Button-Frame - button_frame = tk.Frame(content) - button_frame.pack(fill=tk.X, pady=(15, 0)) - - # Online-Link Button - online_btn = tk.Button( - button_frame, - text="📖 Detaillierte Anleitung online", - command=lambda: webbrowser.open("https://git.file-archive.de/webfarben/pdf_to_ics"), - bg="#3498db", - fg="white", - font=("Arial", 10, "bold"), - padx=20, - pady=10, - cursor="hand2" - ) - online_btn.pack(side=tk.LEFT, padx=(0, 10)) - - # Close Button - close_btn = tk.Button( - button_frame, - text="Schließen", - command=guide_window.destroy, - bg="#95a5a6", - fg="white", - font=("Arial", 10, "bold"), - padx=20, - pady=10, - cursor="hand2" - ) - close_btn.pack(side=tk.LEFT) - - def show_about_dialog(self): - """Zeige About-Dialog mit Programminformationen""" - about_window = tk.Toplevel(self.root) - about_window.title("Über dieses Programm") - about_window.geometry("500x400") - about_window.resizable(False, False) - - # Zentriere das Fenster - about_window.transient(self.root) - about_window.grab_set() - - # Header - header = tk.Label( - about_window, - text="PDF zu ICS Konverter", - font=("Arial", 16, "bold"), - bg="#2c3e50", - fg="white", - pady=15 - ) - header.pack(fill=tk.X) - - # Content Frame - content = tk.Frame(about_window, padx=20, pady=20) - content.pack(fill=tk.BOTH, expand=False) - - # Version - version = get_current_version() - version_label = tk.Label( - content, - text=f"Version {version}", - font=("Arial", 11, "bold"), - fg="#2c3e50" - ) - version_label.pack(anchor=tk.W, pady=(0, 15)) - - # Info-Texte - info_texts = [ - ("Firma:", "Webfarben"), - ("Programmierer:", "Sebastian Köhler"), - ("Kontakt:", "kontakt@webfarben.de"), - ] - - for label, value in info_texts: - frame = tk.Frame(content) - frame.pack(anchor=tk.W, pady=3, fill=tk.X) - - label_widget = tk.Label( - frame, - text=label, - font=("Arial", 10, "bold"), - width=15, - anchor=tk.W - ) - label_widget.pack(side=tk.LEFT) - - value_widget = tk.Label( - frame, - text=value, - font=("Arial", 10), - fg="#34495e" - ) - value_widget.pack(side=tk.LEFT, padx=(5, 0)) - - # Git Repository mit Link - repo_frame = tk.Frame(content) - repo_frame.pack(anchor=tk.W, pady=(15, 0), fill=tk.X) - - repo_label = tk.Label( - repo_frame, - text="Repository:", - font=("Arial", 10, "bold"), - width=15, - anchor=tk.W - ) - repo_label.pack(side=tk.LEFT) - - repo_url = "https://git.file-archive.de/webfarben/pdf_to_ics.git" - repo_link = tk.Label( - repo_frame, - text=repo_url, - font=("Arial", 10, "underline"), - fg="#3498db", - cursor="hand2" - ) - repo_link.pack(side=tk.LEFT, padx=(5, 0)) - repo_link.bind("", lambda e: webbrowser.open(repo_url)) - - # Beschreibung - desc_frame = tk.Frame(content) - desc_frame.pack(anchor=tk.W, pady=(20, 0), fill=tk.BOTH, expand=False) - - desc_text = tk.Text( - desc_frame, - height=4, - font=("Arial", 9), - fg="#34495e", - wrap=tk.WORD, - relief=tk.FLAT, - bg=about_window.cget("bg") - ) - desc_text.insert(tk.END, - "Ein Programm zur Konvertierung von Dienstplan-PDFs " - "zu ICS-Kalenderdateien für einfaches Importieren " - "in Kalenderprogramme.") - desc_text.config(state=tk.DISABLED) - desc_text.pack(fill=tk.BOTH, expand=False) - - # Close Button - close_btn = tk.Button( - about_window, - text="Schließen", - command=about_window.destroy, - bg="#3498db", - fg="white", - font=("Arial", 10, "bold"), - padx=50, - pady=12, - cursor="hand2" - ) - close_btn.pack(pady=(10, 15), fill=tk.X, padx=20) - - - def save_config(self): - """Speichere Konfiguration""" - try: - config = { - 'last_output_dir': self.output_dir.get(), - 'last_pdf_dir': self.last_pdf_dir, - 'exclude_rest': self.exclude_rest.get() - } - with open(CONFIG_FILE, 'w') as f: - json.dump(config, f, indent=2) - except Exception as e: - print(f"Warnung: Konfiguration konnte nicht gespeichert werden: {e}") - - def on_closing(self): - """Handle für Fenster schließen""" - self.save_config() - self.root.destroy() - - def setup_drag_and_drop(self): - """Richte Drag & Drop ein""" - if HAS_TKINTERDND: - # Verwende tkinterdnd2 wenn verfügbar - self.pdf_listbox.drop_target_register(DND_FILES) - self.pdf_listbox.dnd_bind('<>', self.drop_files) - else: - # Fallback: Native Tkinter Drag & Drop (funktioniert auf Unix) - try: - self.pdf_listbox.drop_target_register('DND_Files') - self.pdf_listbox.dnd_bind('<>', self.drop_files) - except: - # Wenn auch das nicht funktioniert, zeige Hilfstext - pass - - def drop_files(self, event): - """Handle für Drag & Drop Events""" - files = [] - - if HAS_TKINTERDND: - # Parse tkinterdnd2 format - files_str = event.data - # Entferne geschweifte Klammern und splitte - files_str = files_str.strip('{}') - files = re.findall(r'[^\s{}]+(?:\s+[^\s{}]+)*', files_str) - else: - # Fallback parsing - if hasattr(event, 'data'): - files_str = event.data.strip('{}') - files = [f.strip() for f in files_str.split()] - - # Füge nur PDF-Dateien hinzu - pdf_count = 0 - for file_path in files: - file_path = file_path.strip() - if file_path.lower().endswith('.pdf'): - if file_path not in self.pdf_files: - self.pdf_files.append(file_path) - self.pdf_listbox.insert(tk.END, Path(file_path).name) - pdf_count += 1 - - if pdf_count > 0: - self.log(f"✓ {pdf_count} PDF-Datei(en) per Drag & Drop hinzugefügt") - elif files: - self.log("⚠ Nur PDF-Dateien können hinzugefügt werden") - - return 'break' - - 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""" - # Starte im letzten PDF-Verzeichnis - initial_dir = self.last_pdf_dir - if initial_dir.startswith('.') or not Path(initial_dir).exists(): - initial_dir = str(Path.home()) - - files = filedialog.askopenfilenames( - title="PDF-Dateien auswählen", - filetypes=[("PDF Dateien", "*.pdf"), ("Alle Dateien", "*.*")], - initialdir=initial_dir - ) - - 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: - # Merke Verzeichnis der ersten ausgewählten Datei - self.last_pdf_dir = str(Path(files[0]).parent) - 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""" - # Verhindere Start in versteckten Verzeichnissen - initial_dir = self.output_dir.get() - if initial_dir.startswith('.') or '/.venv' in initial_dir or '/__pycache__' in initial_dir: - initial_dir = str(Path.home()) - - directory = filedialog.askdirectory( - title="Ausgabe-Verzeichnis auswählen", - initialdir=initial_dir, - mustexist=True - ) - - if directory: - self.output_dir.set(directory) - self.save_config() # Sofort speichern - 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), exclude_rest=self.exclude_rest.get()) - - 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 check_for_updates_background(self): - """Prüfe auf Updates im Hintergrund""" - try: - update_available, new_version, download_url = check_for_updates() - - if update_available: - # Zeige Update-Dialog auf dem Main-Thread - self.root.after(0, self.show_update_dialog, new_version, download_url) - except Exception as e: - # Stille Fehler ignorieren, damit GUI nicht beeinflusst wird - pass - - def show_update_dialog(self, new_version, download_url): - """Zeige Update-Dialog""" - current_version = get_current_version() - - response = messagebox.showinfo( - "Update verfügbar", - f"Eine neue Version ist verfügbar!\n\n" - f"Aktuelle Version: v{current_version}\n" - f"Neue Version: v{new_version}\n\n" - f"Möchten Sie die neue Version herunterladen?" - ) - - if response == 'ok': # Dialog mit OK-Button - import webbrowser - webbrowser.open(download_url) - - -def main(): - """Hauptfunktion""" - if HAS_TKINTERDND: - # Verwende TkinterDnD root für besseres Drag & Drop - root = TkinterDnD.Tk() - else: - # Standard Tkinter - root = tk.Tk() - - app = PDFtoICSGUI(root) - root.mainloop() - - -if __name__ == '__main__': - main() diff --git a/gui_wxpython.py b/gui_wxpython.py index 7690298..9b27443 100644 --- a/gui_wxpython.py +++ b/gui_wxpython.py @@ -6,7 +6,7 @@ Native Benutzeroberfläche für macOS, Windows, Linux """ import wx -import wx.lib.scrolledpanel as scrolled +import wx.adv from pathlib import Path import threading import json @@ -100,7 +100,8 @@ class PDFtoICSFrame(wx.Frame): config = { 'last_output_dir': self.output_dir, 'last_pdf_dir': self.last_pdf_dir, - 'exclude_rest': self.exclude_rest_checkbox.GetValue() + 'exclude_rest': self.exclude_rest_checkbox.GetValue(), + 'exclude_vacation': self.exclude_vacation_checkbox.GetValue() } with open(CONFIG_FILE, 'w') as f: json.dump(config, f, indent=2) @@ -201,6 +202,13 @@ class PDFtoICSFrame(wx.Frame): ) self.exclude_rest_checkbox.SetValue(self.config.get('exclude_rest', False)) content_sizer.Add(self.exclude_rest_checkbox, 0, wx.ALL, 10) + + self.exclude_vacation_checkbox = wx.CheckBox( + panel, + label='🏖️ Urlaub ausschließen (060, 0060)' + ) + self.exclude_vacation_checkbox.SetValue(self.config.get('exclude_vacation', False)) + content_sizer.Add(self.exclude_vacation_checkbox, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) # Log-Bereich log_label = wx.StaticText(panel, label='Status:') @@ -351,7 +359,8 @@ class PDFtoICSFrame(wx.Frame): create_ics_from_dienstplan( dienstplan, str(ics_path), - exclude_rest=self.exclude_rest_checkbox.GetValue() + exclude_rest=self.exclude_rest_checkbox.GetValue(), + exclude_vacation=self.exclude_vacation_checkbox.GetValue() ) self.log(f" ✓ ICS erstellt: {ics_filename}") diff --git a/install.sh b/install.sh index d933bc5..96cca69 100755 --- a/install.sh +++ b/install.sh @@ -96,40 +96,6 @@ else print_success "venv ist bereits installiert" fi -# Prüfe und installiere Tkinter wenn nötig -print_step "Prüfe Tkinter-Installation..." -if ! python3 -c "import tkinter" 2>/dev/null; then - print_warning "Tkinter ist nicht installiert. Installation wird versucht..." - - # Erkenne Distribution - if [ -f /etc/debian_version ]; then - echo "Debian/Ubuntu erkannt. Installiere python3-tk..." - if command -v sudo &> /dev/null; then - sudo apt-get update && sudo apt-get install -y python3-tk - else - print_error "sudo nicht verfügbar. Bitte installieren Sie python3-tk manuell:" - echo " apt-get install python3-tk" - exit 1 - fi - elif [ -f /etc/fedora-release ]; then - echo "Fedora erkannt. Installiere python3-tkinter..." - sudo dnf install -y python3-tkinter - elif [ -f /etc/arch-release ]; then - echo "Arch Linux erkannt. Installiere tk..." - sudo pacman -S --noconfirm tk - else - print_warning "Distribution nicht erkannt. Bitte installieren Sie Tkinter manuell." - echo "Möchten Sie trotzdem fortfahren? (y/n)" - read -r response - if [[ ! "$response" =~ ^[Yy]$ ]]; then - exit 1 - fi - fi - print_success "Tkinter installiert" -else - print_success "Tkinter ist bereits installiert" -fi - # Erstelle Installationsverzeichnis print_step "Erstelle Installationsverzeichnis..." mkdir -p "$INSTALL_DIR" @@ -151,7 +117,19 @@ print_success "Virtual Environment erstellt" print_step "Installiere Python-Abhängigkeiten..." .venv/bin/pip install -q --upgrade pip .venv/bin/pip install -q pdfplumber icalendar pypdf2 pytz packaging -.venv/bin/pip install -q tkinterdnd2 2>/dev/null || print_warning "tkinterdnd2 optional nicht installiert (kein Problem)" + +print_step "Installiere wxPython..." +if .venv/bin/pip install wxPython; then + print_success "wxPython installiert" +else + print_error "wxPython konnte nicht installiert werden." + print_warning "Auf Linux fehlen ggf. Build-Abhängigkeiten. Unter Debian/Ubuntu/Mint oft hilfreich:" + echo " sudo apt-get update" + echo " sudo apt-get install -y build-essential python3-dev libgtk-3-dev libglib2.0-dev libjpeg-dev libtiff-dev libpng-dev" + echo "Danach die Installation erneut starten: ./install.sh" + exit 1 +fi + print_success "Abhängigkeiten installiert" # Erstelle Launcher-Script @@ -161,7 +139,7 @@ cat > "$LAUNCHER" << 'EOF' # PDF zu ICS Konverter Launcher INSTALL_DIR="$HOME/.local/share/pdf-to-ics" cd "$INSTALL_DIR" -exec .venv/bin/python gui.py +exec .venv/bin/python gui_wxpython.py EOF chmod +x "$LAUNCHER" print_success "Launcher erstellt: $LAUNCHER" diff --git a/pdf_to_ics.py b/pdf_to_ics.py index 2674203..93747eb 100644 --- a/pdf_to_ics.py +++ b/pdf_to_ics.py @@ -154,7 +154,7 @@ def parse_dienstplan_table(table, month_start_str): return events -def create_ics_from_dienstplan(dienstplan, output_path=None, exclude_rest=False): +def create_ics_from_dienstplan(dienstplan, output_path=None, exclude_rest=False, exclude_vacation=False): """ Erstellt eine ICS-Datei aus den Dienstplan-Daten @@ -162,6 +162,7 @@ def create_ics_from_dienstplan(dienstplan, output_path=None, exclude_rest=False) dienstplan: Dictionary mit Dienstplan-Daten output_path: Pfad für Output-Datei exclude_rest: Wenn True, werden Ruhepausen nicht exportiert + exclude_vacation: Wenn True, wird Urlaub (060/0060) nicht exportiert """ # Erstelle Calendar cal = Calendar() @@ -182,12 +183,13 @@ def create_ics_from_dienstplan(dienstplan, output_path=None, exclude_rest=False) continue service_type = event_data['service'] + normalized_service_type = service_type.lstrip('0') or '0' event = Event() # Titel - nur den Dienstart # Spezielle Service-Typen mit aussagekräftigen Namen - if service_type == '0060': + if normalized_service_type == '60': title = "Urlaub" elif service_type in rest_types: title = "Ruhe" @@ -197,6 +199,10 @@ def create_ics_from_dienstplan(dienstplan, output_path=None, exclude_rest=False) # Überspringe Ruhepausen wenn gewünscht (nach Titel-Erstellung) if exclude_rest and title == "Ruhe": continue + + # Überspringe Urlaub wenn gewünscht (060/0060) + if exclude_vacation and title == "Urlaub": + continue event.add('summary', title) @@ -268,6 +274,7 @@ Beispiele: python3 pdf_to_ics.py # Konvertiere alle PDFs im aktuellen Verzeichnis python3 pdf_to_ics.py --input ./pdfs --output ./ics # PDFs aus ./pdfs → ICS zu ./ics python3 pdf_to_ics.py --input ./pdfs --exclude-rest # Schließe Ruhetage aus + python3 pdf_to_ics.py --input ./pdfs --exclude-vacation # Schließe Urlaub (060) aus python3 pdf_to_ics.py file.pdf # Konvertiere einzelne Datei """ ) @@ -296,6 +303,12 @@ Beispiele: action='store_true', help='Ruhetage ausschließen (Ruhe, R56, R36, vRWF48, RWE, vR48)' ) + + parser.add_argument( + '-u', '--exclude-vacation', + action='store_true', + help='Urlaub ausschließen (060, 0060)' + ) parser.add_argument( '-v', '--verbose', @@ -336,6 +349,8 @@ Beispiele: print(f"📄 PDF-Dateien gefunden: {len(pdf_files)}") if args.exclude_rest: print("🧘 Ruhetage werden ausgeschlossen") + if args.exclude_vacation: + print("🏖️ Urlaub (060) wird ausgeschlossen") print() success_count = 0 @@ -362,7 +377,12 @@ Beispiele: # Erstelle ICS-Datei ics_filename = pdf_file.stem + '.ics' ics_path = output_dir / ics_filename - create_ics_from_dienstplan(dienstplan, str(ics_path), exclude_rest=args.exclude_rest) + create_ics_from_dienstplan( + dienstplan, + str(ics_path), + exclude_rest=args.exclude_rest, + exclude_vacation=args.exclude_vacation + ) print(f" ✓ ICS erstellt: {ics_path}") success_count += 1 diff --git a/start_gui.cmd b/start_gui.cmd index 45f8f39..4526e95 100644 --- a/start_gui.cmd +++ b/start_gui.cmd @@ -23,9 +23,24 @@ if errorlevel 1 ( echo ✓ Abhängigkeiten installiert ) +REM Überprüfe, ob wxPython installiert ist +.venv\Scripts\python.exe -c "import wx" 2>nul +if errorlevel 1 ( + echo 📚 Installiere wxPython... + call .venv\Scripts\python.exe -m pip install -q wxPython + if errorlevel 1 ( + echo. + echo ❌ wxPython konnte nicht installiert werden. + echo Bitte installieren Sie Visual C++ Build Tools und versuchen Sie es erneut. + pause + exit /b 1 + ) + echo ✓ wxPython installiert +) + REM Starte die GUI echo 🎨 Starte GUI... -call .venv\Scripts\pythonw.exe gui.py +call .venv\Scripts\pythonw.exe gui_wxpython.py if errorlevel 1 ( echo. diff --git a/start_gui.sh b/start_gui.sh index 5183e15..143e6bc 100755 --- a/start_gui.sh +++ b/start_gui.sh @@ -31,8 +31,8 @@ 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 +# Überprüfe, ob Kern-Abhängigkeiten installiert sind +if ! $PYTHON_VENV -c "import pdfplumber, icalendar, pypdf2, pytz, packaging" 2>/dev/null; then echo "📚 Installiere Abhängigkeiten..." if $PYTHON_VENV -m pip install -q pdfplumber icalendar pypdf2 pytz packaging 2>/dev/null; then @@ -53,16 +53,25 @@ if ! $PYTHON_VENV -c "import pdfplumber" 2>/dev/null; then fi fi -# Versuche tkinterdnd2 zu installieren (optional für Drag & Drop) -if ! $PYTHON_VENV -c "import tkinterdnd2" 2>/dev/null; then - echo "💡 Installiere tkinterdnd2 für Drag & Drop (optional)..." - $PYTHON_VENV -m pip install -q tkinterdnd2 2>/dev/null && echo "✓ Drag & Drop aktiviert" || echo "ℹ️ Drag & Drop nicht verfügbar (kein Problem)" +# Überprüfe, ob wxPython installiert ist +if ! $PYTHON_VENV -c "import wx" 2>/dev/null; then + echo "📚 Installiere wxPython..." + if $PYTHON_VENV -m pip install wxPython; then + echo "✓ wxPython installiert" + else + echo "❌ wxPython konnte nicht installiert werden" + echo "💡 Auf Linux fehlen ggf. Build-Abhängigkeiten. Unter Debian/Ubuntu/Mint oft hilfreich:" + echo " sudo apt-get update" + echo " sudo apt-get install -y build-essential python3-dev libgtk-3-dev libglib2.0-dev libjpeg-dev libtiff-dev libpng-dev" + echo " rm -rf .venv && ./start_gui.sh" + exit 1 + fi fi # Starte die GUI echo "🎨 Starte GUI..." if [ -f "$PYTHON_VENV" ]; then - $PYTHON_VENV gui.py + $PYTHON_VENV gui_wxpython.py else echo "❌ Fehler: Python-Umgebung ist beschädigt" echo "📁 Bitte löschen Sie das .venv Verzeichnis und versuchen Sie erneut:" diff --git a/version.txt b/version.txt index 9084fa2..26aaba0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.1.0 +1.2.0