Windows desktop autoclicker: Tkinter GUI assembly, AutoclickerController for lifecycle, background click worker threads, settings in %APPDATA%/WindowsAutoclicker/, automation via pyautogui / mouse / keyboard.
autoclicker.py # Root shim: imports autoclicker.main:main
autoclicker/
main.py # Entry: logging setup, AutoclickerApp().run()
app/
controller.py # Settings, engine, picker, start/stop, session log
hotkeys.py # Global F6/F7/ESC registration + unregister
tray.py # pystray icon
gui/
main_window.py # Window shell; marshals worker callbacks via root.after
sections/ # Section builders: title, coordinates, click_settings,
# advanced (collapsible), control, status bar, disclaimer
# + collapsible.py (reusable disclosure frame)
core/
settings_manager.py # Validation + atomic JSON persistence
settings_paths.py # AppData path + legacy migration
click_engine.py # Click loop, queue, safety guards
safety.py # Failsafe + fail-closed foreground window helpers
session_log.py # Append-only session log
resources.py # Frozen/source asset paths
logging_setup.py # stderr + AppData rotating log
exceptions.py
utils/
coordinate_picker.py
| Thread / scheduler | Owner | Role |
|---|---|---|
| Main thread | Tkinter | UI event loop, AutoclickerApp.run() |
| Click thread | ClickEngine.start_clicking |
Daemon thread running _click_loop |
| Queue processor | ClickEngine (optional) |
Daemon thread when queuing enabled |
| Tray thread | app.tray |
Daemon thread running pystray icon |
| Status timer | Tk root.after(1000, ...) |
Periodic status label updates |
All worker threads are daemon threads so process exit does not block on them.
Worker callbacks (hotkeys, tray, picker, click complete, safety stop) are marshaled onto the Tk thread with root.after(0, ...). The click thread never join()s itself.
| Path | Clears is_running |
Joins click thread | Stops queue |
|---|---|---|---|
stop_clicking |
yes | yes, if not the click thread | yes (sentinel) |
emergency_stop |
yes | no | yes (drop pending + sentinel) |
| runaway / failsafe | yes | UI joins from Tk thread | yes |
| max_clicks / auto_stop | yes (finally) |
via notify_click_complete |
yes |
- Settings:
SettingsManageratomically writes%APPDATA%/WindowsAutoclicker/autoclicker_settings.json(legacy CWD file migrated once). - GUI: Sections bind Tk widgets;
AutoclickerControllervalidates and starts/stops clicking. - Click engine: Coordinates, interval, burst, safety limits;
pyautoguiwithPAUSE=0; optional queue path. Waits use_stop_event.wait. - Session log: Start/stop/safety events appended under AppData.
- Screen input:
CoordinatePickerusesmouse+ ESC cancel; presets viaPresetManager.
pyautogui,mouse,keyboard: input automationpywin32: foreground window check (safety.py)Pillow,pystray: tray icontkinter: GUI (stdlib)sv-ttk: Sun Valley ttk theme (light/dark)
- GUI / logic split:
AutoclickerController(app/controller.py) owns settings, engine, picker, and presets;gui/sections/*only build widgets and forward values, keeping the UI replaceable without touching core logic. - Safety defaults: PyAutoGUI failsafe is on by default, with a runaway clicks-per-second ceiling and an optional pause when the foreground window changes (fail-closed if the HWND cannot be read). All stops are recorded in the session log.
- Performance: O(1) Welford running stats back the 1 Hz status poll instead of recomputing aggregates over the timing history (see PERFORMANCE.md). A Win32
SendInputhot path was evaluated and deferred (no measurable win overpyautoguiwithPAUSE=0, plus multi-monitor DPI risk).
Unit tests under tests/ (pytest). Coverage gate on the autoclicker package (cov-fail-under=65, gui/sections omitted). scripts/smoke_check.py verifies imports outside pytest and runs in CI on Python 3.11.