Files
exif-tool/exif_gui.pyw
2026-03-18 15:47:02 +01:00

156 lines
6.3 KiB
Python

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()