commit 0b20932b0eb134a07e9192ff8ab4695b3513f0b8 Author: Robert Heiserer Date: Wed Mar 18 15:47:02 2026 +0100 intial commit diff --git a/BilderDatum.bat b/BilderDatum.bat new file mode 100644 index 0000000..0686535 --- /dev/null +++ b/BilderDatum.bat @@ -0,0 +1,7 @@ +@echo off +:: 'start' entkoppelt den Prozess vom Terminal. +:: Die leeren Anfuehrungszeichen "" sind wichtig, falls der Pfad Leerzeichen hat. +start "" pythonw "C:\Program Files\exiftool-13.52_64\DatumAnpassen\exif_gui.pyw" + +:: Das Terminal schliesst sich jetzt sofort +exit \ No newline at end of file diff --git a/exif_gui.pyw b/exif_gui.pyw new file mode 100644 index 0000000..646176f --- /dev/null +++ b/exif_gui.pyw @@ -0,0 +1,156 @@ +import os +import subprocess +import threading +import tkinter as tk +from tkinter import filedialog, messagebox, ttk +from datetime import datetime, timedelta +from concurrent.futures import ThreadPoolExecutor + +# Versuche tkcalendar zu importieren +try: + from tkcalendar import DateEntry +except ImportError: + print("Bitte installiere tkcalendar: pip install tkcalendar") + exit() + +class ExifApp: + def __init__(self, root): + self.root = root + self.root.title("ExifTool - Turbo Sequenzer") + self.root.geometry("500x450") + self.root.resizable(False, False) + + # DEIN STANDARDPFAD + default_path = r"Z:\PC_WIN_10\Laufwerk_D\Eigene Dateien\Eigene Bilder" + + # Prüfen, ob der Pfad existiert, sonst leer lassen + initial_folder = default_path if os.path.exists(default_path) else "" + + # Variablen + self.folder_var = tk.StringVar(value=initial_folder) + self.time_var = tk.StringVar(value="12:00:00") + self.offset_var = tk.StringVar(value="5") + self.status_var = tk.StringVar(value="Bereit") + self.progress_var = tk.DoubleVar() + + self.setup_ui() + + def select_folder(self): + # Beim Öffnen des Dialogs auch direkt im Standardpfad starten + current_val = self.folder_var.get() + initial_dir = current_val if os.path.exists(current_val) else None + + path = filedialog.askdirectory(initialdir=initial_dir) + if path: + self.folder_var.set(path) + + def setup_ui(self): + frame = tk.Frame(self.root, padx=20, pady=20) + frame.pack(fill="both", expand=True) + + # 1. Ordner + tk.Label(frame, text="1. Ordner wählen:", font=("Arial", 10, "bold")).pack(anchor="w") + f_frame = tk.Frame(frame) + f_frame.pack(fill="x", pady=(5, 15)) + tk.Entry(f_frame, textvariable=self.folder_var, state="readonly").pack(side="left", fill="x", expand=True, padx=(0, 10)) + tk.Button(f_frame, text="Durchsuchen", command=self.select_folder).pack(side="right") + + # 2. Datum & Zeit + tk.Label(frame, text="2. Start-Zeitpunkt:", font=("Arial", 10, "bold")).pack(anchor="w") + dt_frame = tk.Frame(frame) + dt_frame.pack(fill="x", pady=(5, 15)) + self.cal = DateEntry(dt_frame, width=12, background='darkblue', date_pattern='dd.mm.yyyy') + self.cal.pack(side="left", padx=(0, 15)) + tk.Label(dt_frame, text="Uhrzeit:").pack(side="left") + tk.Entry(dt_frame, textvariable=self.time_var, width=10).pack(side="left", padx=(5, 0)) + + # 3. Versatz + tk.Label(frame, text="3. Zeitversatz (Sekunden):", font=("Arial", 10, "bold")).pack(anchor="w") + off_frame = tk.Frame(frame) + off_frame.pack(fill="x", pady=(5, 15)) + tk.Entry(off_frame, textvariable=self.offset_var, width=8).pack(side="left") + + # Fortschrittsbalken + tk.Label(frame, text="Fortschritt:").pack(anchor="w", pady=(10, 0)) + self.progress_bar = ttk.Progressbar(frame, variable=self.progress_var, maximum=100) + self.progress_bar.pack(fill="x", pady=(5, 20)) + + # Start Button + self.btn_start = tk.Button(frame, text="Batch-Verarbeitung starten", command=self.start_thread, + font=("Arial", 11, "bold"), bg="#2196F3", fg="white", pady=8) + self.btn_start.pack(fill="x") + + # Status + tk.Label(frame, textvariable=self.status_var, fg="#666").pack(pady=10) + + def start_thread(self): + # Validierung + if not self.folder_var.get(): + messagebox.showerror("Fehler", "Kein Ordner gewählt!") + return + + self.btn_start.config(state="disabled") + threading.Thread(target=self.run_process, daemon=True).start() + + def update_exif(self, file_info): + filepath, time_str = file_info + cmd = ["exiftool", "-overwrite_original", f"-AllDates={time_str}", filepath] + try: + if os.name == 'nt': + subprocess.run(cmd, check=True, creationflags=subprocess.CREATE_NO_WINDOW) + else: + subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + return True + except: + return False + + def run_process(self): + folder = self.folder_var.get() + start_date = self.cal.get_date() + + try: + h, m, s = map(int, self.time_var.get().split(':')) + start_dt = datetime.combine(start_date, datetime.min.time()).replace(hour=h, minute=m, second=s) + offset = int(self.offset_var.get()) + except Exception as e: + self.root.after(0, lambda: messagebox.showerror("Fehler", "Ungültige Zeit oder Versatz!")) + self.root.after(0, lambda: self.btn_start.config(state="normal")) + return + + valid_ext = ('.jpg', '.jpeg', '.png', '.tif', '.tiff', '.cr2', '.nef', '.arw') + files = sorted([os.path.join(folder, f) for f in os.listdir(folder) if f.lower().endswith(valid_ext)]) + + if not files: + self.root.after(0, lambda: messagebox.showinfo("Info", "Keine Bilder gefunden.")) + self.root.after(0, lambda: self.btn_start.config(state="normal")) + return + + total = len(files) + tasks = [] + + # Aufgabenliste vorbereiten + for i, path in enumerate(files): + curr_time = start_dt + timedelta(seconds=i * offset) + tasks.append((path, curr_time.strftime("%Y:%m:%d %H:%M:%S"))) + + # Parallelisierung mit ThreadPool + # Nutzt standardmäßig CPU-Kern-Anzahl * 5 (gut für I/O Prozesse wie ExifTool) + self.status_var.set(f"Verarbeite {total} Bilder parallel...") + + completed = 0 + with ThreadPoolExecutor() as executor: + for result in executor.map(self.update_exif, tasks): + completed += 1 + prog = (completed / total) * 100 + self.progress_var.set(prog) + self.status_var.set(f"Bild {completed} von {total} fertig...") + + self.status_var.set("Abgeschlossen!") + self.root.after(0, lambda: messagebox.showinfo("Erfolg", f"Fertig! {total} Bilder wurden aktualisiert.")) + self.root.after(0, lambda: self.btn_start.config(state="normal")) + self.root.after(0, lambda: self.progress_var.set(0)) + +if __name__ == "__main__": + root = tk.Tk() + app = ExifApp(root) + root.mainloop() \ No newline at end of file