Skip to content

TNTGuerrilla/SlideShow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

8 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Slideshow Widget

A borderless Windows desktop slideshow, a "digital picture frame on your desktop." It scans a folder (recursively) for images, cycles through them at a configurable interval with your choice of 40+ transitions, and stays out of the way in the system tray. Built in Python with PySide6, shipped as a single-file .exe.

Status: v1.0, feature complete (Milestone 4). See Docs/milestone-log.md for the milestone history.


Quick start (run the app)

Option A: the built executable (recommended)

Download / build SlideshowWidget.exe (see Building below) and double-click it. No install, no Python required, it's a single portable file.

On first launch, move the mouse to the top-right and open Settings (โš™), then pick your image folder. Everything you change is saved immediately, so the next launch goes straight to your slideshow.

First-run note (SmartScreen): because the binary isn't code-signed, Windows SmartScreen may show an "unrecognized app" prompt the first time you run it (More info โ†’ Run anyway). This is a reputation prompt, not a virus detection, see Antivirus / SmartScreen.

Option B: from source (development)

Prerequisites: Python 3.10+ and uv.

uv sync                  # create the venv from uv.lock
uv run python -m app     # launch

A test/ folder at the repo root (gitignored, supply your own images) is used as the first-run default if you haven't chosen a folder yet.


Controls

The window is borderless (no title bar). Move the mouse near the top-right to reveal the control cluster; it fades when the cursor leaves.

Control Action
๐Ÿ“ Folder Open the current image in Explorer
๐Ÿ“Œ Pin Pin/unpin the current image (stops the cycle on it)
โฎ / โฏ Previous / Next
โฏ Pause Pause / resume the auto-cycle (stays lit while paused)
๐Ÿ—‘ Remove Drop the current image from rotation (file is not deleted)
โš™ Settings Open the settings dialog

Window: drag anywhere to move, drag edges to resize, Win+Arrow to snap to Windows zones, double-click to maximize.

Keyboard (all rebindable in Settings โ†’ Hotkeys): โ†’/โ† next/prev, Space pause, Del remove, P pin, S settings. Esc hides to tray (or quits if no tray); it's intentionally fixed so a borderless window is never un-closable.

System tray: Esc / Alt-F4 hide the window to the tray (the slideshow keeps running there). Click the tray icon to restore; right-click for Show/Hide, Play/Pause, Next, Previous, Settings, and Exit.


Settings

Open with the โš™ button. Changes apply and save instantly (no OK/Apply). Organized into four tabs:

General

  • Folder: the image directory (scanned recursively). A checkbox tree below it lets you exclude individual subfolders at any depth.
  • Duration: seconds each image is shown (after its transition completes).
  • Scaling: Fit (letterbox) / Fill (crop) / Stretch.
  • Transition + Speed: the slide-change animation (40+ styles; see Transitions) and how long it takes.
  • Order: Sequential / Shuffle / Stratified (folder-aware interleave).
  • Show date: overlay the photo's EXIF capture date in the corner.
  • Keep pause on nav: whether manual prev/next clears a pause.
  • Launch at startup (Windows): run the slideshow when you log in.
  • Screensaver + idle minutes: see Screensaver mode.
  • Remove all app data: wipe everything this app stored (see below).

Removed: every image you've removed from rotation, most-recent first. Double-click to preview; Restore to rotation brings one back.

Hotkeys: rebind any action; click a key, press the combo. Duplicate bindings are rejected. Reset to defaults available.

About: version, license, Show README, third-party attributions (with a viewer for each license), disclaimers, and a Cached image folders list (forget a folder's cached state without touching its images).

Where settings are stored

Everything lives in %APPDATA%\SlideshowWidget\:

  • settings.json: all preferences (atomic write; a corrupt file is backed up to settings.json.bad and defaults are used, so it never fails to start).
  • library.db: SQLite cache: known images, the removed/blacklist list, the pinned image, per-folder state, and the shuffle position (so a restart resumes mid-cycle).

The Remove all app data button (Settings โ†’ General) deletes this folder, the launch-at-startup registry entry, and all cached folder state. Your image files are never touched; the .exe is portable, so you delete it yourself.


Features

Display

  • Borderless, resizable, Windows-snap-aware window (not fullscreen).
  • Random or sequential cycling at a configurable interval.
  • Recursive scan of jpg, jpeg, png, webp, gif, bmp.
  • EXIF orientation auto-correction (portrait photos display upright).
  • Fit / Fill / Stretch scaling.

Transitions

  • 40+ styles across 10 families: Fade (cut / crossfade / through-black), Slide, Push (filmstrip), Reveal, Wipe (each in 4 directions + random), Zoom (in/out), Spin & Rotate (CW/CCW), Squish, Carousel (perspective 3D card flip), and a top-level Random that picks a different one each time.
  • Configurable duration, independent of the slide interval. Easing is tuned per family (smooth S-curve for motion, linear for fades).

Dynamic updates

  • watchdog detects images added/removed from the folder live: no restart.
  • Resilient to the folder going away (drive unplugged): a banner shows and the app retries, without losing your removed-list or other state.

Startup

  • A Launch at startup toggle writes/removes an HKCU\โ€ฆ\Run registry entry. (Manual alternative: put a shortcut to the .exe in shell:startup.)

Tray, hotkeys, and polish

  • Minimize-to-tray with a full right-click menu.
  • Fully rebindable hotkeys.
  • Pin an image, exclude subfolders, open-in-Explorer, restore removed images.

Soft requirements (5 implemented)

The assignment asks for at least two; this ships five:

  1. Smooth crossfade between images, and 40+ other transition styles built on the same engine.
  2. Configurable hotkeys, a dedicated Hotkeys tab; every action rebindable, stored in settings.json.
  3. Persistent blacklist for removed images, removals survive restarts (SQLite), with a Removed tab to view/restore them.
  4. Multi-monitor awareness, the screensaver fills every connected monitor, each showing a different image; the windowed slideshow targets the screen it's on and restores its exact placement after the screensaver.
  5. Memory-conscious image loading, a small bounded preload cache decodes at display size (not native), uses Pillow's Image.draft() for fractional JPEG decode, and never holds the whole library in RAM, so memory stays bounded no matter how large the folder is.

Screensaver mode (surprise-me)

The headline bonus feature. After a configurable idle period (Settings โ†’ General โ†’ Screensaver), the app goes borderless-fullscreen on every monitor, hides the cursor and controls, and cycles images with your configured transition, one distinct photo per screen. Any keyboard/mouse input restores the windowed slideshow exactly where it left off.

It behaves like a real screensaver:

  • Idle detection is system-wide (GetLastInputInfo), not app-local.
  • Audio-aware: it won't kick in while an application is playing sound (so a movie or music isn't interrupted); notification dings are ignored.
  • Good citizen: it only suppresses the Windows screensaver / display-sleep while it's showing; at all other times your power settings are untouched.

Building

Requires the dev dependencies (uv sync installs Nuitka). From the repo root:

.\build.ps1                 # onefile build  -> dist\SlideshowWidget.exe
.\build.ps1 -Compiler msvc  # use the MSVC backend (recommended; fewer AV flags)
.\build.ps1 -Standalone     # folder build instead of single-file

The script invokes Nuitka with (in essence):

python -m nuitka --onefile --enable-plugin=pyside6
    --include-package=PIL --include-package-data=PIL
    --include-package=comtypes --include-package=pycaw
    --include-data-dir=assets=assets
    --include-data-dir=LICENSES=LICENSES
    --include-data-files=LICENSE=LICENSE
    --include-data-files=app.ico=app.ico
    --windows-console-mode=disable
    --windows-icon-from-ico=app.ico
    --company-name="TNTGuerrilla" --product-name="Slideshow Widget"
    --file-version=1.0.0.0 --product-version=1.0.0.0
    --onefile-tempdir-spec="{CACHE_DIR}\SlideshowWidget\1.0.0"
    --lto=auto --output-dir=dist --output-filename=SlideshowWidget.exe
    run.py

See build.ps1 for the exact, commented command. The app icon is generated from the tray glyph by uv run python tools/make_icon.py.

Test the build on a clean Windows VM (no Python, no build tools) before relying on it, missing-DLL problems only surface there.


Antivirus / SmartScreen

Nuitka is used precisely because it produces a real compiled binary, which Windows Defender flags far less than PyInstaller's unpack-and-reexec bootloader. The build also carries full version/company metadata, a real icon, never requests admin elevation, and unpacks to a stable cached directory, all of which reduce heuristic false positives.

The one thing that can't be fixed without a code-signing certificate is the SmartScreen "unrecognized app" prompt on a brand-new binary. That's a reputation prompt, not a virus detection; it clears once the binary is signed or has been downloaded enough. To sign it yourself: signtool sign /fd SHA256 /a dist\SlideshowWidget.exe.


Why PySide6?

The GUI framework is Qt 6 via PySide6 (the official Qt for Python binding), chosen over PyQt6 for license freedom: PySide6 is LGPL v3, so this application's own code can be licensed however the author chooses (here, MIT), whereas PyQt6 is GPL v3 and would force the whole app to be GPL. Functionally the two are interchangeable; the slideshow uses only standard Qt features. The lighter pyside6-essentials distribution is used (no 3D/Charts/WebEngine).

Other choices: Pillow for image decode (C-backed codecs, Image.draft() for fast JPEG), watchdog for live file detection, SQLite (WAL) for the cache, pycaw for the screensaver's audio detection, and Nuitka for packaging. Full rationale in Docs/tech-stack.md.


Licensing

This project's own code is released under the MIT License (see LICENSE).

It is distributed with third-party components under their own licenses, with full texts bundled in LICENSES/ and viewable in-app (About tab):

Component License
PySide6 / Qt LGPL v3 (see LICENSES/PySide6-NOTICE.txt for the source-offer + re-linking notice)
Pillow HPND
watchdog Apache 2.0
pycaw, comtypes MIT

LGPL note: the Qt/PySide6 binaries ship as dynamically-loadable modules; a user may substitute their own compatible build of PySide6/Qt. Qt source is at https://code.qt.io/cgit/pyside/pyside-setup.git/.


Project layout

app/                  Application source
  __main__.py         Entry point (settings โ†” library โ†” cache โ†” window wiring)
  window.py           Borderless main window, nav cluster, tray, date overlay
  stage.py            Shared two-label transition surface (window + screensaver)
  transitions.py      40+ transition styles + animated label
  screensaver.py      Idle/audio detection + multi-monitor screensaver
  cache.py            Decoded-image preload cache (memory-conscious)
  decoder.py          Background decode (Pillow / Qt) + EXIF
  library.py          DB + queue + scanner + watcher coordination
  shuffle.py          Sequential / shuffle / stratified ordering
  db.py               SQLite cache (images, queue, roots)
  config.py           JSON settings (atomic, tolerant, migrating)
  settings_dialog.py  Tabbed settings (General / Removed / Hotkeys / About)
  tray.py             System-tray icon + menu
  startup.py          Launch-at-startup registry helper
  shell.py            Explorer / default-app open helpers
  watcher.py          watchdog observer โ†’ debounced GUI events
  win32.py            Borderless chrome (resize / snap / drag)
  nav_widget.py       Control cluster   toggle_switch.py  Material toggle
build.ps1             Nuitka build script        run.py   build entry point
tools/make_icon.py    Generates app.ico from the tray glyph
Docs/                 Spec, phase plans, tech stack, milestone log, known issues
LICENSES/             Bundled third-party license texts

Documentation

About

A super lightweight slideshow app that seamlessly integrates with Windows

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors