Smart File Organizer is a desktop tool for automatically sorting files by type and category. It includes a modern graphical interface (GUI) with Light / Dark Mode support, a command-line interface (CLI), real Undo / Redo based on recorded history, hash-based duplicate detection, and real-time folder monitoring using watchdog.
| Feature | Description |
|---|---|
| 🧩 Modular Architecture | Rebuilt from one large file into 19 focused modules (mixins), each owning a single concern — theme, sorting, preview, watchdog, settings, etc. |
| 🧵 Real Thread Safety | Introduced a central, thread-safe update queue (ui_queue_mixin.py) so the sort worker, undo/redo workers, and the watchdog thread never touch Tkinter widgets directly — eliminating a real RuntimeError: main thread is not in main loop crash and silent progress-bar freezes. |
| 🎯 Correct Duplicate Routing | Fixed a bug where a duplicate file could be moved to the wrong destination (a leftover path from a previous loop iteration) instead of its intended Duplicates/<category>/ folder. |
| ♻️ True Idempotency | Re-running Sort on an already-sorted folder now moves zero files. Previously it would re-shuffle Others/ files with endless (1), (2)... suffixes and nest Duplicates/ folders inside themselves on every run. |
| 🖼️ Flicker-Free Live Preview | The preview grid now reuses existing widgets (widget pooling) instead of destroying and rebuilding hundreds of them on every refresh. |
| 🌗 Light / Dark Mode | Instant switching between modes, with automatic saving to organizer_settings.json and no more crash on repeated toggling. |
| 💾 Settings Persistence | Automatic loading and saving of the last used folder, filters, and user preferences — tolerant of a corrupted settings file. |
| 🔄 Undo / Redo | Full restoration of sorting actions based on a recorded history in .sort_history.json. |
| 🧠 Duplicate Handling | Detects byte-identical files by SHA-256 content hash (not filename) and routes them into Duplicates/<category>/. |
| 👀 Real-Time Monitoring | Automatically tracks folder changes using watchdog, paused during an active sort to avoid feedback loops. |
| 🧪 Dry-Run | Preview upcoming actions before actually executing them, for testing and safety. |
| 💻 CLI Mode | Run from the command line with --no-gui, no window required — for automation or scripting. |
| 📝 Logging | Full logging of all sorting actions to sorted_files_log.txt, for auditing and recovery. |
| File | Description |
|---|---|
main.py | Entry point. Launches the GUI, or runs headless via --no-gui for CLI use. |
app.py | SmartOrganizerApp — composes every mixin below into the final application class. |
file_sorter.py | Core sorting logic: category detection, SHA-256 duplicate hashing, moving files, and the Undo/Redo history engine. |
theme_mixin.py | Dark/Light theme switching and all ttk style definitions. |
ui_mixin.py | Builds the entire window layout (header, folder picker, options, actions, stats, log, preview). |
folder_mixin.py | Browsing for and opening the target folder in the OS file explorer. |
sort_mixin.py | Runs the sort operation on a background thread. |
preview_mixin.py | The live thumbnail grid, using widget pooling to stay flicker-free. |
watchdog_mixin.py | Starts/stops live filesystem monitoring of the selected folder. |
undo_redo_mixin.py | Undo and Redo of the last sort operation. |
settings_mixin.py | JSON settings persistence (auto-save) and the Settings window. |
stats_progress_mixin.py | Updates the stat cards and the progress bar. |
log_mixin.py | Thread-safe activity-log queue and its display. |
ui_queue_mixin.py | ★ The central thread-safe channel every background thread uses to request a UI update. |
colors.py | Dark/Light color palettes. |
icons.py | Generates file/folder thumbnail icons for the preview grid. |
tooltip.py | Hover tooltip widget. |
watchdog_handler.py | FileSystemEventHandler used by the watchdog observer. |
logging_setup.py | Logger configuration and file paths (LOG_FILE, SETTINGS_FILE). |
organizer_settings.json | (generated) User settings: theme, last folder, filters — loaded/saved automatically. |
.sort_history.json | (generated, inside the sorted folder) Sort action history that powers Undo/Redo. |
| Area | Feature | Status | Notes |
|---|---|---|---|
| 📂 File Sorting | Automatic classification by type | ✅ | Images, documents, code, video, audio, and more |
| 🧪 Safety | Dry-Run | ✅ | Preview before actually moving files |
| 🧠 Duplicates | Content-hash detection | ✅ | SHA-256, routed into Duplicates/<category>/ |
| 🔄 Recovery | Undo / Redo | ✅ | Full restoration from history |
| ♻️ Repeatability | Idempotent re-sorting | ✅ | Re-running Sort moves 0 files on an already-sorted folder |
| 👀 Monitoring | Real-time folder tracking | ✅ | Powered by watchdog, thread-safe |
| 💻 CLI | Command-line execution | ✅ | Includes advanced flags |
| 🎨 Interface | Light / Dark Mode | ✅ | Automatically saved, crash-free toggling |
| 🧵 Concurrency | Thread-safe UI updates | ✅ | Central queue — no direct cross-thread Tk calls |
| Category | Icon | Supported Extensions |
|---|---|---|
| Images | 🖼️ | .jpg .jpeg .png .gif .bmp .tiff .webp .heic |
| Documents | 📄 | .pdf .docx .doc .txt .odt .rtf |
| Code | 💻 | .py .java .cpp .c .h .js .html .css .ts .go .rb |
| Videos | 🎥 | .mp4 .mkv .avi .mov .wmv .flv |
| Audio | 🎵 | .mp3 .wav .aac .ogg .flac |
| Archives | 📦 | .zip .rar .tar .gz .7z |
| Spreadsheets | 📊 | .xls .xlsx .csv |
| Presentations | 📈 | .ppt .pptx |
| Others | ❓ | Any unrecognized extension |
| Duplicates/<category> | 🧬 | Any file whose SHA-256 hash matches one already sorted |
| Step | Action | Command |
|---|---|---|
| 1️⃣ | Install dependencies | pip install ttkbootstrap Pillow watchdog |
| 2️⃣ | Run the interface | python main.py |
Run the tool directly from the terminal for automation or headless (no-GUI) operation:
python main.py <folder> --no-gui [--dry-run] [--include-hidden] [--duplicates]Sorting has to run off the main thread so the window doesn't freeze — but Tkinter widgets, variables, and root.after(...) may only be touched from the main thread. Three background threads used to break that rule.
| Background thread | What it used to do wrong | Fix |
|---|---|---|
| Sort worker | Called root.after(...) and read Tkinter Variable.get() directly | Values captured on the main thread first; updates now go through ui_queue |
| Undo / Redo workers | Called root.after(...) directly to refresh stats/preview | Routed through ui_queue |
| Watchdog observer | Called root.after(...) / after_cancel(...) on every filesystem event | Routed through ui_queue; bursts of events are naturally coalesced into one refresh per poll |
All three now only push plain data into a queue.Queue. A single poller, scheduled exclusively via root.after() on the main thread, drains it and applies every update safely.
- It's recommended to start with a Dry-Run
- Files already sorted into their category folder (including
OthersandDuplicates) are skipped on the next run — re-sorting is safe and idempotent - Categories can be extended in
file_sorter.py→FILE_CATEGORIES
This project is distributed under the MIT license – free to use, modify, and distribute with credit.
👨💻 Raz Eini (2025)