AutoDateInserter is a desktop app that batch-renames files by prepending or appending timestamps.
It supports preview, undo, dry-run, pause/resume, drag & drop, and progress tracking.
This tutorial walks through the app step by step, making it beginner-friendly and easy to understand.
π§ What Youβll Learn
How to structure a class-based Tkinter app
How to safely batch-rename files
How to add previews, undo, and dry-run
How to keep the UI responsive with threads + queues
How to design a clean UI using ttkbootstrap
π¦ Project Structure
Everything lives in one Python file for simplicity:
AutoDateInserter.py
Dependencies:
pip install ttkbootstrap tkinterdnd2
1οΈβ£ Imports & Feature Detection
We start by importing everything we need.
import os, sys, threading, platform, json, time
import tkinter as tk
from tkinter import filedialog
from datetime import datetime
import ttkbootstrap as tb
from ttkbootstrap.constants import *
import queue
import logging
Optional Drag & Drop Support
try:
from tkinterdnd2 import TkinterDnD, DND_FILES
DND_ENABLED = True
except ImportError:
DND_ENABLED = False
π‘ Why?
If tkinterdnd2 isnβt installed, the app still works β just without drag & drop.
2οΈβ£ App Class & Window Setup
We wrap the entire app in a class to keep things clean and scalable.
class AutoDateInserterApp:
APP_NAME = "AutoDateInserter"
APP_VERSION = "1.2.0"
Create the Window
self.root = TkinterDnD.Tk() if DND_ENABLED else tk.Tk()
tb.Style(theme="darkly")
self.root.title(f"{self.APP_NAME} v{self.APP_VERSION}")
self.root.geometry("1120x700")
π¨ ttkbootstrap instantly gives us a modern dark theme.
3οΈβ£ App Storage & Logging
We store logs and undo history in a user-safe directory.
self.app_dir = os.path.join(
os.path.expanduser("~"),
"AppData", "Local", self.APP_NAME
)
os.makedirs(self.app_dir, exist_ok=True)
Enable Logging
log_file = os.path.join(self.app_dir, "rename.log")
logging.basicConfig(
filename=log_file,
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
π§ This lets us debug rename failures later.
4οΈβ£ App State & Thread Safety
self.rename_map = {}
self.start_time = None
self.pause_event = threading.Event()
self.pause_event.set()
self.is_paused = False
Thread-Safe UI Updates
self.ui_queue = queue.Queue()
π‘ Important:
Tkinter is not thread-safe, so background threads send updates through a queue.
5οΈβ£ Tkinter Variables (Reactive UI)
self.mode_var = tk.StringVar(value="prepend")
self.recursive_var = tk.BooleanVar(value=False)
self.dry_run_var = tk.BooleanVar(value=False)
self.progress_var = tk.IntVar(value=0)
self.eta_var = tk.StringVar(value="ETA: --:--")
self.speed_var = tk.StringVar(value="0.00 files/sec")
These automatically update UI widgets when changed.
6οΈβ£ Utility Functions
Timestamp Generator
def timestamp(self):
return datetime.now().strftime("%Y%m%d_%H%M%S")
Safe Rename (Avoid Overwrites)
def safe_name(self, path):
base, ext = os.path.splitext(path)
i = 1
new = path
while os.path.exists(new):
new = f"{base} ({i}){ext}"
i += 1
return new
β Prevents accidental file loss.
7οΈβ£ Building the UI (High-Level)
The UI is broken into logical sections:
Title
File list
Options
Preview
Controls
def _build_ui(self):
tb.Label(self.root, text=self.APP_NAME,
font=("Segoe UI", 22, "bold")).pack()
File List + Scrollbar
self.listbox = tk.Listbox(box, selectmode=tk.EXTENDED)
self.listbox.pack(side="left", fill="both", expand=True)
Drag & Drop Support
if DND_ENABLED:
self.listbox.drop_target_register(DND_FILES)
self.listbox.dnd_bind("<<Drop>>", self.on_drop)
8οΈβ£ Adding Files & Folders
Add Files
def add_files(self):
for f in filedialog.askopenfilenames():
self.listbox.insert(tk.END, f)
Add Folder (Recursive Optional)
def collect_files(self, folder):
if self.recursive_var.get():
for r, _, files in os.walk(folder):
for f in files:
self.listbox.insert(tk.END, os.path.join(r, f))
9οΈβ£ Preview Before Renaming (Critical Safety Feature)
def build_preview(self):
self.rename_map.clear()
ts = self.timestamp()
Generate New Names
new = (
f"{ts}_{base}{ext}"
if self.mode_var.get() == "prepend"
else f"{base}_{ts}{ext}"
)
Show Preview
self.preview.insert(
tk.END, f"{name} β {os.path.basename(dst)}\n"
)
π Users see exactly what will happen before committing.
π Renaming Files (Threaded)
threading.Thread(
target=self.rename_files,
daemon=True
).start()
Rename Logic
os.rename(src, dst)
undo[dst] = src
Progress + ETA
speed = i / elapsed
eta = (total - i) / speed
1οΈβ£1οΈβ£ Pause / Resume Support
self.pause_event.wait()
def toggle_pause(self):
self.is_paused = not self.is_paused
β Useful for large batches.
1οΈβ£2οΈβ£ Undo System (JSON-Based)
undo_file = f"undo_{timestamp}.json"
json.dump(undo, f, indent=2)
Restore Files
os.rename(dst, src)
π₯ This makes the app safe for production use.
1οΈβ£3οΈβ£ Why This Architecture Works
β
Class-based
β
Thread-safe
β
Undoable
β
Preview-first
β
Beginner-readable
β
Production-ready
π Final Thoughts
AutoDateInserter shows how far Tkinter can go when combined with:
Good structure
Safety features
Modern styling
Thoughtful UX
If youβre learning Python desktop apps β this is a perfect real-world project.
π Source Code
GitHub:
π https://github.com/rogers-cyber/AutoDateInserter

Top comments (0)