intial commit
This commit is contained in:
7
BilderDatum.bat
Normal file
7
BilderDatum.bat
Normal file
@@ -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
|
||||
156
exif_gui.pyw
Normal file
156
exif_gui.pyw
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user