Skip to main content

Lightweight photo and video viewer with editing, PDF export and plugins.

Project description

myImages

A lightweight desktop viewer, editor and converter for photos and videos, built with PySide6. It looks like a compact, dark, icon-driven gallery (a close cousin of the sibling myPhotos project) but adds a tool panel, inline image editing, PDF/GIF export and preview, duplicate finding, batch rename and a plugin system.

The runtime core is deliberately tiny — PySide6 + Pillow. Everything else (ffmpeg for video, trash support, HEIC) is optional and can be installed from inside the app.

Features

  • Browse a folder, optionally including its sub-folders. The list keeps itself up to date when files are added, deleted or edited by other programs, and only the thumbnails that actually changed are rebuilt.
  • Preview photos with fit / wheel-zoom / pan, and videos with Qt Multimedia (falls back to a note + the video tools when the backend is absent).
  • Navigate with ←/→ or the mouse wheel; Shift+wheel zooms and never zooms out past the fit-to-window size.
  • File list on a panel that can sit on either side: three views (thumbnail grid, plain names, or a sortable table with clickable headers), a name-search box, favourites (F, shown as a star), and multi-select. Its width is set in whole thumbnail columns — one to four — so the grid always fills the panel instead of leaving a ragged strip of empty space.
  • Edit images inline under the preview (double-click a thumbnail, or the pencil icon): rotate, mirror horizontally or vertically, pick an aspect ratio that locks the crop box, Crop, then Save (overwrite) or Save as Copy; plus format conversion (JPEG/PNG/WebP/BMP/TIFF/GIF) and colour → black & white.
  • Remove a watermark: the badge generators stamp into a corner is detected and painted over with its surroundings. Draw a selection first to clean a mark anywhere else. The result opens in the editor, so nothing is written until you approve it.
  • Remove the background: switch the editor to Cut out (or use the wand on the toolbar). The first control finds the subject with a model and clears everything else in one press; it needs the optional bgremove extra and a one-off download, and the app offers both when you press it. The result is just another step in the edit list, so Undo removes it and the hand tools below correct whatever it got wrong. Click a colour with the magic wand to clear its region, drag the eraser to take more away and the restore brush to paint the picture back, and soften the edge. Hold Compare to see the original, and cycle the backdrop between a checkerboard, white, black and magenta to spot a leftover fringe. Every step is undoable one at a time. Saving over a JPEG writes a PNG beside it, since a JPEG cannot hold transparency; the button says so before you press it.
  • Right-click a photo or a thumbnail for Copy File, Copy Filename, Copy Picture, Delete, Rename, Select, Edit Image, Rotate, Convert, Remove Watermark and Remove Background. Rename here renames that one file; the toolbar keeps the pattern-based batch rename for a whole selection.
  • Images → PDF: combine a selection into one PDF with control over page quality, maximum edge and greyscale, plus an optional target file size the builder meets by lowering quality automatically.
  • Video tools (via ffmpeg): trim on a timeline, crop the sides, scale, and export a GIF of the trimmed range.
  • GIF from frames: build an animated GIF from several selected images.
  • Find duplicates: exact (content hash) and visually similar (perceptual hash, adjustable) with one-click bulk delete of the extras.
  • Batch rename with a filename mask and a live preview (one file at a time is the plain box on the right-click menu).
  • Delete to the system trash when Send2Trash is installed.
  • Plugins: drop a *.py viewer into the plugins folder to preview new file types (a 3D-model example ships in myimages/plugins/).
  • Themes (dark/light), remembered layout, on-disk thumbnail cache.
  • Desktop integration (Settings → Desktop): add myImages to the application menu and make it the default for the photo and video types it opens. Both install into your own ~/.local/share, need no root, and can be undone from the same place. Opening a photo from a file manager loads its whole folder, so you can step through the gallery from there.

Run from source

Requires Python 3.10+ and (for video features) ffmpeg on the PATH.

python3 -m venv .venv && source .venv/bin/activate
pip install -e .
python main.py          # or: myimages

main.py in the repository root is just a thin shell around myimages.app.main; all the real code lives in the myimages package.

Pressing Ctrl+C in the launching terminal closes the app the normal way, so the window layout and settings are still saved on the way out.

Optional features

Open Optional features (the puzzle icon) in the app to see what is missing and install the Python ones (Send2Trash, pillow-heif) with a click. ffmpeg is a system tool — install it with your package manager:

sudo apt install ffmpeg

Develop

pip install -e ".[dev,trash,heif]"
pre-commit install
make check          # ruff + mypy + pytest (>90% coverage gate)

Individual gates: make lint, make typecheck, make test, make format.

The code style is strict on purpose: ruff, black, mypy --strict, self-documenting names (no leading-underscore identifiers) and a docstring on every public function. The pure logic (imaging, video, scanning, dedup, rename) is fully unit-tested; the PySide6 UI is tested offscreen with pytest-qt.

Build a package

pip install pyinstaller
make deb        # -> myimages_<ver>_<arch>.deb   (installs under /opt/myimages)
make appimage   # -> myImages-<ver>-x86_64.AppImage

Both freeze the application with PyInstaller from one shared spec (packaging/myimages.spec) and wrap the result, so each artifact carries its own Python and needs none on the machine it lands on. They also carry the segmentation runtime, because a packaged build cannot pip-install into itself; the model weights are still fetched on first use. Only the Qt system libraries are expected from the host, and the Debian package declares them.

On Windows, pwsh -File packaging/build_windows.ps1 produces myImages-<ver>-windows-x64.zip. Unpack it anywhere and run myimages.exe; everything the app keeps lives under %USERPROFILE%\.myimages, so deleting the folder removes it completely.

The Windows build is unsigned, so SmartScreen shows "Windows protected your PC" the first time. Choose More info, then Run anyway. Signing needs a certificate that now requires hardware or a cloud signing service; it is not worth it for this project yet.

On macOS, bash packaging/build_macos.sh produces myImages-<ver>-macos-<arch>.dmg. Open it and drag the app to Applications. Apple Silicon and Intel are separate images: there is no universal2 build of PySide6, so each architecture is built on its own machine.

The macOS build is ad-hoc signed but not notarised, so a copy downloaded through a browser is quarantined and Gatekeeper reports it as damaged. Clear the quarantine flag once:

xattr -dr com.apple.quarantine /Applications/myImages.app

Since macOS 15 the old right-click → Open bypass no longer works for unsigned apps, so this is the way. Proper signing needs an Apple Developer account; it is not worth it for this project yet.

FFmpeg is not bundled — the prebuilt binaries are GPL, which would change the licence of the whole distribution. It stays a Recommends, and the app names the right install command for the platform you are on.

Project layout

Path Purpose
main.py Shell entry point (python main.py)
myimages/app.py QApplication bootstrap: logging, theme, plugins
myimages/config.py JSON-backed settings model
myimages/theme.py, icons.py Dark/light theme and painted vector icons
myimages/core/ Media model, scanning, thumbnails, dedup, rename, deletion, folder watching, plugins, dependencies
myimages/imaging/ Convert, transform (crop/scale/rotate/grey), PDF
myimages/video/ ffmpeg wrapper, trim, crop/scale, GIF
myimages/gui/ PySide6 window, panels, viewers and tool dialogs
myimages/plugins/ Bundled example plugins (3D model info)
tests/ Pytest suite (offscreen, synthetic assets)
packaging/ .deb / AppImage build scripts and .desktop

See the roadmap for the plugin API and planned work.

Storage

Everything the app persists lives under ~/.myimages/:

  • settings.json — all preferences and favourites,
  • thumbnails/ — the on-disk thumbnail cache,
  • plugins/ — drop your own *.py viewer plugins here.

Point MYIMAGES_DATA_DIR elsewhere to relocate all of it (the test suite uses this to stay isolated).

Watching the folder

While a folder is open the app follows changes made to it elsewhere. A check reads the directory listing only — never the pictures — so it stays cheap: a folder of 3 000 photos (428 MB) costs about 114 ms per check, on a worker thread, every 10 s by default. The operating system also reports changes instantly, so the timer is only a safety net for network shares.

Optionally (Settings → Also verify file checksums) a handful of files per check are also md5-summed. That catches a file rewritten without changing its size or date, which no amount of stat-ing can see. Only a small rotating batch is hashed per pass, so the whole folder is still covered over time without ever re-reading every photo at once — hashing 3 000 photos in one pass would cost roughly 0.9 s and 428 MB of reads.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

myimages-0.0.2.tar.gz (223.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

myimages-0.0.2-py3-none-any.whl (150.4 kB view details)

Uploaded Python 3

File details

Details for the file myimages-0.0.2.tar.gz.

File metadata

  • Download URL: myimages-0.0.2.tar.gz
  • Upload date:
  • Size: 223.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for myimages-0.0.2.tar.gz
Algorithm Hash digest
SHA256 259655fcb62fd750f4a26ffa2450bcee58d0f42130d865bec1f1b8376948b783
MD5 ab1c0e474cb9efbc41311e2a33842c9f
BLAKE2b-256 6c91a8e6a30fe5c5d9c762bddb4ff06d2e44e832e86a11a6187bcd21db6ac9a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for myimages-0.0.2.tar.gz:

Publisher: publish.yml on wachawo/myimages

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file myimages-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: myimages-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 150.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for myimages-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d8a0da0cc3c1bbfa6498a7d2790606df9fbee12f9ac5511256986371bcabd8b2
MD5 01210079ef8f8563833a859fbb38c573
BLAKE2b-256 60fcef6c01b683ff4e6dd92e3c64f4614c35e47d193727d5ac94e6459023388a

See more details on using hashes here.

Provenance

The following attestation bundles were made for myimages-0.0.2-py3-none-any.whl:

Publisher: publish.yml on wachawo/myimages

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page