spacr.qt.iconset

Central icon lookup for the spacr Qt GUI.

Two sources of icons, both made theme-aware here:

  • qtawesome glyphs — vector, painted in whatever colour we ask for. Wrapped so callers stay decoupled from Font Awesome glyph names. When the optional font is unavailable a bundled, theme-aware glyph is returned; toolbar affordances never silently turn into blank buttons.

  • bundled PNGs in spacr/resources/icons — flat monochrome artwork baked at a fixed colour.

The bundled PNGs were theme-blind, and it showed: convert.png (Format Converter) shipped as solid black with an alpha mask, so on the black home page it rendered as nothing at all. The other twenty-odd were solid white, which is the same bug pointed the other way — invisible on the light theme, nobody had noticed because nobody used it. A third theme made this unavoidable, so themed_qimage() now re-inks every bundled PNG for the active theme. This is what makes the artwork swappable: seventeen icons were redrawn and reinstalled without a line of change here, because nothing in this module knows what any of them look like.

  • the artwork’s ink polarity is detected from its own alpha-weighted mean luminance, so black-on-transparent and white-on-transparent are both handled without a per-file table;

  • the ink is remapped onto a band running from the theme’s foreground down to a veil colour computed to clear MIN_ICON_CONTRAST against the hardest surface the icon can land on, so internal shading survives instead of being flattened to a silhouette;

  • genuinely polychrome artwork keeps its hue and is only re-levelled.

icon_contrast() exposes the measured ratio so the test suite can assert visibility numerically rather than checking that a file exists.

Functions

accent_icon(→ PySide6.QtGui.QIcon)

Icon painted in the accent color (used for primary buttons).

active_theme(→ str)

The theme icons should be drawn for, resolved from preferences.

app_icon(→ PySide6.QtGui.QIcon)

Icon for an app key: the bundled PNG re-inked for the theme,

bundled_icon_path(→ Optional[str])

Resolve an app key to its bundled PNG, or None.

bundled_icon_paths(→ Tuple[str, ...])

Every bundled PNG, sorted. Used by the theme-visibility test.

carries_tonal_structure(→ bool)

True when an icon's RGB channels carry shading worth preserving.

contrast_icon(→ PySide6.QtGui.QIcon)

Icon painted for use inside a filled (PrimaryButton) button,

hardest_surface(→ str)

The surface colour an icon has the least contrast against.

icon(→ PySide6.QtGui.QIcon)

Return a QIcon for the named glyph, with a bundled fallback.

icon_cache_dir(→ pathlib.Path)

Directory holding re-inked icons, one PNG per (file, theme).

icon_contrast(→ float)

Worst-case contrast of a themed icon against the theme's surfaces.

icon_ink_color(→ Optional[str])

Alpha-weighted mean colour of the re-inked artwork, as hex.

reink(rgba, theme)

Re-ink an RGBA array for theme. Returns a uint8 RGBA array.

themed_array(path[, theme])

Re-inked (h, w, 4) uint8 array for path, or None.

themed_pixmap(path[, theme])

themed_qimage() as a QPixmap, or None.

themed_qimage(path[, theme])

Return the bundled PNG at path, re-inked for theme.

veil_color(→ str)

Dimmest ink allowed in a themed icon.

Module Contents

spacr.qt.iconset.accent_icon(name: str, theme: str | None = None) → PySide6.QtGui.QIcon[source]

Icon painted in the accent color (used for primary buttons).

Parameters:

name – semantic icon key, as for icon().

spacr.qt.iconset.active_theme() → str[source]

The theme icons should be drawn for, resolved from preferences.

Falls back to "dark" if preferences can’t be read at all — icon lookup must never be the thing that stops the GUI booting.

spacr.qt.iconset.app_icon(key: str, override: str | None = None, theme: str | None = None) → PySide6.QtGui.QIcon[source]

Icon for an app key: the bundled PNG re-inked for the theme, falling back to the themed qtawesome glyph.

Parameters:

key – application registry key; resolved to bundled artwork by bundled_icon_path(), and used as the icon() name when no artwork is found or it cannot be read.

spacr.qt.iconset.bundled_icon_path(key: str, override: str | None = None) → str | None[source]

Resolve an app key to its bundled PNG, or None.

Tried in order: the caller’s override, <key>.png, the same with underscores as spaces, and finally whatever SHARED_ICON_ASSETS says this key borrows.

Parameters:
  • key – application registry key whose bundled artwork is requested; it is also used verbatim to form the first conventional filename.

  • override – explicit filename to try first. The per-screen key → filename table lives in spacr.qt.app next to the app registry it describes; this module only knows how to render what it’s pointed at, plus the handful of keys in SHARED_ICON_ASSETS that share one picture everywhere.

spacr.qt.iconset.bundled_icon_paths() → Tuple[str, ...][source]

Every bundled PNG, sorted. Used by the theme-visibility test.

spacr.qt.iconset.carries_tonal_structure(rgba) → bool[source]

True when an icon’s RGB channels carry shading worth preserving.

Measured, not assumed. Every bundled spaCR icon is a monochrome mask: the alpha channel holds the entire shape and the RGB is a uniform fill (white for most of them, black for convert.png). For those, the RGB carries no information at all and the right answer is to paint the alpha mask in the theme’s ink.

A couple of assets (umap.png, activation.png) genuinely do put shading in RGB, and flattening those to a silhouette would destroy the picture. The discriminator is whether the visible-pixel luminance spans a meaningful fraction of the range — 2 % of variation is noise from whatever exported the file, not shading.

Parameters:

rgba – RGBA pixel array of shape (H, W, 4) with 0-255 values; only pixels whose alpha exceeds 2 % are measured, and a fully transparent array gives False.

spacr.qt.iconset.contrast_icon(name: str, theme: str | None = None) → PySide6.QtGui.QIcon[source]

Icon painted for use inside a filled (PrimaryButton) button, where the button background IS the accent fill.

Parameters:

name – semantic icon key, as for icon().

spacr.qt.iconset.hardest_surface(theme: str) → str[source]

The surface colour an icon has the least contrast against.

Parameters:

theme – theme name, one of spacr.qt.theme.THEMES; the theme’s fg ink is compared against each role in ICON_SURFACES.

spacr.qt.iconset.icon(name: str, color: str | None = None, size: int = 16, theme: str | None = None) → PySide6.QtGui.QIcon[source]

Return a QIcon for the named glyph, with a bundled fallback.

name is a semantic key (e.g. “open”, “run”, “brush”) mapped to a Font Awesome glyph. Unknown names fall back to a puzzle piece. The default fill follows the active theme rather than the dark palette, which is why a light-theme sidebar no longer draws pale-grey icons on white.

Parameters:

name – semantic icon key looked up in the module’s glyph table, e.g. "open" or "run".

spacr.qt.iconset.icon_cache_dir() → pathlib.Path[source]

Directory holding re-inked icons, one PNG per (file, theme).

spacr.qt.iconset.icon_contrast(path: str, theme: str | None = None) → float[source]

Worst-case contrast of a themed icon against the theme’s surfaces.

0.0 when the file can’t be read or is fully transparent.

Parameters:

path – path to the icon artwork (PNG or SVG); its modification time and size key both the in-memory and the on-disk cache.

spacr.qt.iconset.icon_ink_color(path: str, theme: str | None = None) → str | None[source]

Alpha-weighted mean colour of the re-inked artwork, as hex.

This is the colour the eye integrates when the icon is small, which is what makes it the right thing to measure contrast on.

Parameters:

path – path to the icon artwork (PNG or SVG); its modification time and size key both the in-memory and the on-disk cache.

spacr.qt.iconset.reink(rgba, theme: str)[source]

Re-ink an RGBA array for theme. Returns a uint8 RGBA array.

The alpha channel is the shape, always. RGB is consulted only when carries_tonal_structure() says it holds real shading, so swapping in different artwork later cannot break this — a redrawn monochrome mask keeps working with no code change.

Parameters:
  • rgba – (h, w, 4) float array, channels 0-255.

  • theme – theme name.

spacr.qt.iconset.themed_array(path: str, theme: str | None = None)[source]

Re-inked (h, w, 4) uint8 array for path, or None.

Parameters:

path – path to the icon artwork (PNG or SVG); its modification time and size key both the in-memory and the on-disk cache.

spacr.qt.iconset.themed_pixmap(path: str, theme: str | None = None)[source]

themed_qimage() as a QPixmap, or None.

Parameters:

path – path to the icon artwork (PNG or SVG); its modification time and size key both the in-memory and the on-disk cache.

spacr.qt.iconset.themed_qimage(path: str, theme: str | None = None)[source]

Return the bundled PNG at path, re-inked for theme.

None when the file can’t be read. Returns a QImage, which (unlike QPixmap) needs no running QGuiApplication, so this is safe to call from a headless test.

Parameters:

path – path to the icon artwork (PNG or SVG); its modification time and size key both the in-memory and the on-disk cache.

spacr.qt.iconset.veil_color(theme: str) → str[source]

Dimmest ink allowed in a themed icon.

Solved, not guessed: bisect the blend from the hardest surface toward the theme foreground until the contrast crosses MIN_ICON_CONTRAST. That way the shadow end of an icon’s tonal range is still a visible shape, and the answer tracks the palette instead of being a magic grey someone eyeballed once.

Parameters:

theme – theme name, one of spacr.qt.theme.THEMES; the result is cached per theme.

Nested helpers

_blend.ch(c)

One hex colour as its three integer channels.

spacr/qt/iconset.py:219