Coverage for graphica/gui/icon_utils.py: 100.0%
28 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
1"""Tabler Icons(MIT)の SVG を QIcon にする。stroke="currentColor" を色に置き換えて描く。"""
2import os
3from functools import lru_cache
5from PySide6.QtCore import QByteArray, QSize, Qt
6from PySide6.QtGui import QIcon, QPixmap, QPainter
7from PySide6.QtSvg import QSvgRenderer
9from graphica.gui.resources import resource_path
11ICONS_DIR = os.path.join("assets", "icons")
14def icon(name, color=None, size=16):
15 """assets/icons/{name}.svg。color を省くとテーマの text_secondary(呼ぶたびに決まる)。"""
16 if color is None:
17 from graphica.gui import theme
18 color = theme.current_tokens()["text_secondary"]
19 # exe では同梱のアイコンが sys._MEIPASS の下にあるので、このファイルの場所ではなく resource_path() で探す
20 svg_path = resource_path(os.path.join(ICONS_DIR, f"{name}.svg"))
21 return load_svg_icon(svg_path, color=color, size=size)
24@lru_cache(maxsize=None)
25def _load_svg_text(svg_path):
26 with open(svg_path, "r", encoding="utf-8") as f:
27 return f.read()
30def load_svg_icon(svg_path, color="#4B5157", size=24):
31 """ファイルが無ければ空の QIcon(アイコンが無くてもアプリを落とさない)。"""
32 if not os.path.exists(svg_path):
33 return QIcon()
35 svg_text = _load_svg_text(svg_path).replace("currentColor", color)
36 renderer = QSvgRenderer(QByteArray(svg_text.encode("utf-8")))
38 pixmap = QPixmap(QSize(size, size))
39 pixmap.fill(Qt.GlobalColor.transparent)
40 painter = QPainter(pixmap)
41 renderer.render(painter)
42 painter.end()
44 return QIcon(pixmap)