7 Commits

Author SHA1 Message Date
4b18cd9495 chore: Version auf 1.1.0 erhöht
Neue Features in dieser Version:
- Android iPD Export-Anleitung
- Flexible CLI-Argumente
- Verbesserte UI
2026-02-23 20:02:56 +01:00
d1d1788a3c feat: CLI-Argumente und verbesserte Kommandozeilenverarbeitung
- Add argparse für flexible CLI-Optionen
- Add --input/-i für Eingabe-Verzeichnis
- Add --output/-o für Ausgabe-Verzeichnis
- Add --exclude-rest/-e für Ruhetage ausschließen
- Add --verbose/-v für detaillierte Ausgabe
- Unterstütze einzelne Datei als Argument
- Bessere Fehlerbehandlung und Zusammenfassung
- Update README.md mit CLI-Dokumentation und Beispielen
- Add Optionen-Tabelle für schnelle Referenz
2026-02-23 20:01:10 +01:00
fde69adcec feat: Android Export Guide und UI Verbesserungen
- Add Android iPD Export-Anleitung im Hilfe-Menü
- Fix About Dialog Layout und Close Button Sichtbarkeit
- Fensteröffnungshöhe auf 400px optimiert
- UTF-8 Encoding Deklaration für korrekte Umlaute
2026-02-23 19:51:49 +01:00
44857c6a3c style: Increase close button size in about dialog
- Increase padding and height of close button
- Make button fill full width with side padding
- Better visual balance in the dialog window
2026-02-23 18:56:50 +01:00
9173e59e1e fix: Add -e flag to echo commands in install.sh for proper color code interpretation
- Lines 211-212 now use echo -e to interpret ANSI color codes
- Colors will now display correctly in terminal output during installation
2026-02-23 18:48:12 +01:00
fa59ef5e8a refactor: Improve about dialog with better layout and clickable repository link
- Convert about dialog from messagebox to custom Toplevel window
- Improve text formatting and readability with proper spacing
- Make Git repository link clickable (opens in browser with webbrowser module)
- Better layout with aligned labels and structured information display
- Add close button for better user experience
2026-02-23 18:41:43 +01:00
10674a1454 feat: Add about dialog with program information in help menu
- Added Help menu with "About this program" option
- Displays company (Webfarben), developer (Sebastian Köhler), contact email
- Shows Git repository URL and current software version
- About dialog accessible from menu bar
2026-02-23 18:26:33 +01:00
5 changed files with 390 additions and 21 deletions

View File

@@ -98,16 +98,51 @@ pip install pdfplumber icalendar pypdf2 pytz
## Verwendung ## Verwendung
### Schnellstart ### Schnellstart (CLI)
1. Kopieren Sie Ihre Dienstplan-PDF-Dateien in dieses Verzeichnis 1. Kopieren Sie Ihre Dienstplan-PDF-Dateien in ein Verzeichnis
2. Führen Sie das Skript aus: 2. Führen Sie das Skript aus:
```bash ```bash
python3 pdf_to_ics.py python3 pdf_to_ics.py
``` ```
Das Tool findet automatisch alle `.pdf` Dateien und erstellt entsprechende `.ics` Dateien. Das Tool findet automatisch alle `.pdf` Dateien im aktuellen Verzeichnis und erstellt entsprechende `.ics` Dateien.
### Kommandozeilen-Optionen
```bash
# Alle PDFs im aktuellen Verzeichnis konvertieren
python3 pdf_to_ics.py
# PDFs aus einem bestimmten Verzeichnis konvertieren
python3 pdf_to_ics.py --input ./pdfs
# PDFs in anderes Verzeichnis speichern
python3 pdf_to_ics.py --input ./pdfs --output ./ics_dateien
# Ruhetage ausschließen
python3 pdf_to_ics.py --exclude-rest
# Einzelne PDF-Datei konvertieren
python3 pdf_to_ics.py /pfad/zur/datei.pdf
# Mit detaillierter Ausgabe
python3 pdf_to_ics.py --input ./pdfs -v
# Hilfe anzeigen
python3 pdf_to_ics.py --help
```
**Verfügbare Optionen:**
| Option | Kurzform | Beschreibung |
|--------|----------|-------------|
| `--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) |
| `--verbose` | `-v` | Detaillierte Ausgabe anzeigen |
| `--help` | `-h` | Hilfe anzeigen |
### Erweiterte Nutzung ### Erweiterte Nutzung

242
gui.py
View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
GUI für PDF zu ICS Konverter GUI für PDF zu ICS Konverter
Grafische Benutzeroberfläche mit Tkinter Grafische Benutzeroberfläche mit Tkinter
@@ -10,6 +11,7 @@ from pathlib import Path
import threading import threading
import re import re
import json import json
import webbrowser
from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan
from update_checker import check_for_updates, get_current_version from update_checker import check_for_updates, get_current_version
@@ -37,6 +39,9 @@ class PDFtoICSGUI:
# Variablen # Variablen
self.pdf_files = [] self.pdf_files = []
# Erstelle Menüleiste
self.create_menu()
# Nutze letztes Ausgabeverzeichnis oder Standard # Nutze letztes Ausgabeverzeichnis oder Standard
default_dir = self.config.get('last_output_dir', None) default_dir = self.config.get('last_output_dir', None)
if not default_dir or not Path(default_dir).exists(): if not default_dir or not Path(default_dir).exists():
@@ -246,6 +251,243 @@ class PDFtoICSGUI:
print(f"Warnung: Konfiguration konnte nicht geladen werden: {e}") print(f"Warnung: Konfiguration konnte nicht geladen werden: {e}")
return {} 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("<Button-1>", 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): def save_config(self):
"""Speichere Konfiguration""" """Speichere Konfiguration"""
try: try:

View File

@@ -208,8 +208,8 @@ print_success "Die Anwendung wurde installiert!"
echo "" echo ""
echo "Sie können die Anwendung nun starten:" echo "Sie können die Anwendung nun starten:"
echo "" echo ""
echo " 1. ${BLUE}Über das Anwendungsmenü${NC} (suchen Sie nach 'PDF zu ICS')" echo -e " 1. ${BLUE}Über das Anwendungsmenü${NC} (suchen Sie nach 'PDF zu ICS')"
echo " 2. ${BLUE}Über die Kommandozeile:${NC} pdf-to-ics" echo -e " 2. ${BLUE}Über die Kommandozeile:${NC} pdf-to-ics"
echo "" echo ""
echo "Installation Details:" echo "Installation Details:"
echo " • Installationsverzeichnis: $INSTALL_DIR" echo " • Installationsverzeichnis: $INSTALL_DIR"

View File

@@ -255,39 +255,131 @@ def create_ics_from_dienstplan(dienstplan, output_path=None, exclude_rest=False)
def main(): def main():
""" """
Hauptfunktion Hauptfunktion mit CLI-Argumenten
""" """
import sys import sys
import argparse
# Finde alle PDF-Dateien im aktuellen Verzeichnis parser = argparse.ArgumentParser(
pdf_files = list(Path('.').glob('*.pdf')) description='PDF zu ICS Konverter - Konvertiere Dienstplan-PDFs zu iCalendar-Dateien',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
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 file.pdf # Konvertiere einzelne Datei
"""
)
parser.add_argument(
'pdf_file',
nargs='?',
help='Einzelne PDF-Datei zum Konvertieren (optional)'
)
parser.add_argument(
'-i', '--input',
type=str,
default='.',
help='Eingabe-Verzeichnis mit PDF-Dateien (Standard: aktuelles Verzeichnis)'
)
parser.add_argument(
'-o', '--output',
type=str,
help='Ausgabe-Verzeichnis für ICS-Dateien (Standard: Eingabe-Verzeichnis)'
)
parser.add_argument(
'-e', '--exclude-rest',
action='store_true',
help='Ruhetage ausschließen (Ruhe, R56, R36, vRWF48, RWE, vR48)'
)
parser.add_argument(
'-v', '--verbose',
action='store_true',
help='Detaillierte Ausgabe'
)
args = parser.parse_args()
# Bestimme Eingabe-Verzeichnis
if args.pdf_file:
# Einzelne Datei
pdf_file = Path(args.pdf_file)
if not pdf_file.exists():
print(f"✗ Fehler: Datei nicht gefunden: {pdf_file}")
sys.exit(1)
pdf_files = [pdf_file]
input_dir = pdf_file.parent
else:
# Verzeichnis
input_dir = Path(args.input)
if not input_dir.exists():
print(f"✗ Fehler: Verzeichnis nicht gefunden: {input_dir}")
sys.exit(1)
pdf_files = sorted(input_dir.glob('*.pdf'))
# Bestimme Ausgabe-Verzeichnis
output_dir = Path(args.output) if args.output else input_dir
output_dir.mkdir(parents=True, exist_ok=True)
if not pdf_files: if not pdf_files:
print("Keine PDF-Dateien gefunden!") print(f"Keine PDF-Dateien gefunden in: {input_dir}")
return return
if args.verbose:
print(f"📂 Eingabe-Verzeichnis: {input_dir}")
print(f"📂 Ausgabe-Verzeichnis: {output_dir}")
print(f"📄 PDF-Dateien gefunden: {len(pdf_files)}")
if args.exclude_rest:
print("🧘 Ruhetage werden ausgeschlossen")
print()
success_count = 0
error_count = 0
for pdf_file in pdf_files: for pdf_file in pdf_files:
print(f"\nVerarbeite: {pdf_file}") print(f"\nVerarbeite: {pdf_file.name}")
try: try:
# Extrahiere Daten # Extrahiere Daten
dienstplan = extract_dienstplan_data(str(pdf_file)) dienstplan = extract_dienstplan_data(str(pdf_file))
print(f"Name: {dienstplan['vorname']} {dienstplan['name']}") if args.verbose:
print(f"Personalnummer: {dienstplan['personalnummer']}") print(f" Name: {dienstplan['vorname']} {dienstplan['name']}")
print(f"Betriebshof: {dienstplan['betriebshof']}") print(f" Personalnummer: {dienstplan['personalnummer']}")
print(f"Anzahl der Events: {len(dienstplan['events'])}") print(f" Betriebshof: {dienstplan['betriebshof']}")
print(f" Anzahl der Events: {len(dienstplan['events'])}")
if not dienstplan['events']:
print(f" ⚠️ Warnung: Keine Events gefunden!")
error_count += 1
continue
# Erstelle ICS-Datei # Erstelle ICS-Datei
ics_path = pdf_file.with_suffix('.ics') ics_filename = pdf_file.stem + '.ics'
create_ics_from_dienstplan(dienstplan, str(ics_path)) ics_path = output_dir / ics_filename
create_ics_from_dienstplan(dienstplan, str(ics_path), exclude_rest=args.exclude_rest)
print(f"✓ ICS-Datei erstellt: {ics_path}") print(f" ✓ ICS erstellt: {ics_path}")
success_count += 1
except Exception as e: except Exception as e:
print(f"✗ Fehler bei {pdf_file}: {e}") print(f" ✗ Fehler: {str(e)}")
if args.verbose:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
error_count += 1
# Zusammenfassung
print("\n" + "="*50)
print(f"✅ Fertig!")
print(f" Erfolgreich: {success_count}")
print(f" Fehler: {error_count}")
print("="*50)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1 +1 @@
1.0.0 1.1.0