Source code for spacr.qt.iconset

"""
Central icon lookup for the spacr Qt GUI.

Wraps `qtawesome` so callers stay decoupled from Font Awesome glyph
names, and returns a placeholder QIcon() if qtawesome isn't installed
so the UI still renders (with text-only buttons).
"""
from __future__ import annotations

from functools import lru_cache
from typing import Optional

from PySide6.QtGui import QIcon

from .theme import PALETTE


@lru_cache(maxsize=128)
def _try_qta():
    """Return the ``qtawesome`` module, or ``None`` when it isn't installed."""
    try:
        import qtawesome as qta
        return qta
    except Exception:
        return None


[docs] def icon(name: str, color: Optional[str] = None, size: int = 16) -> QIcon: """Return a QIcon for the named glyph, or an empty QIcon 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. """ qta = _try_qta() if qta is None: return QIcon() glyph = _NAME_TO_GLYPH.get(name, "fa5s.puzzle-piece") fill = color or PALETTE["fg_muted"] try: return qta.icon(glyph, color=fill) except Exception: return QIcon()
[docs] def accent_icon(name: str) -> QIcon: """Icon painted in the accent color (used for primary buttons).""" return icon(name, color=PALETTE["accent"])
[docs] def contrast_icon(name: str) -> QIcon: """Icon painted in the background color — used inside filled (PrimaryButton) buttons where the button bg IS the accent.""" return icon(name, color=PALETTE["bg"])
# Semantic name → Font Awesome glyph. Keep names short + generic so # callers don't have to think about the icon library. _NAME_TO_GLYPH = { # File / source "open": "fa5s.folder-open", "folder": "fa5s.folder", "file": "fa5s.file", "save": "fa5s.save", "import": "fa5s.file-import", "export": "fa5s.file-export", # Navigation "prev": "fa5s.chevron-left", "next": "fa5s.chevron-right", "up": "fa5s.chevron-up", "down": "fa5s.chevron-down", "home": "fa5s.home", "skip": "fa5s.forward", # Editing "brush": "fa5s.paint-brush", "erase": "fa5s.eraser", "erase_object": "fa5s.trash-alt", "wand": "fa5s.magic", "wand_add": "fa5s.plus-circle", "wand_erase": "fa5s.minus-circle", "zoom": "fa5s.search-plus", "zoom_reset": "fa5s.compress-arrows-alt", "undo": "fa5s.undo", "redo": "fa5s.redo", "fill": "fa5s.fill", "invert": "fa5s.adjust", "relabel": "fa5s.tags", "remove": "fa5s.filter", "clear": "fa5s.times-circle", # Actions "run": "fa5s.play", "stop": "fa5s.stop", "settings": "fa5s.cog", "info": "fa5s.info-circle", "check": "fa5s.check", "warning": "fa5s.exclamation-triangle", "chart": "fa5s.chart-bar", "tag": "fa5s.tag", "search": "fa5s.search", # App keys mirrored from app.py for the sidebar / tiles. "mask": "fa5s.mask", "measure": "fa5s.ruler", "annotate": "fa5s.tag", "make_masks": "fa5s.paint-brush", "classify": "fa5s.layer-group", "umap": "fa5s.project-diagram", "ml_analyze": "fa5s.chart-line", "regression": "fa5s.wave-square", "recruitment": "fa5s.crosshairs", "activation": "fa5s.bolt", "analyze_plaques": "fa5s.microscope", "train_cellpose": "fa5s.dumbbell", "cellpose_masks": "fa5s.shapes", "cellpose_all": "fa5s.th", "map_barcodes": "fa5s.barcode", "ai_console": "fa5s.robot", }