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

1"""Tabler Icons(MIT)の SVG を QIcon にする。stroke="currentColor" を色に置き換えて描く。""" 

2import os 

3from functools import lru_cache 

4 

5from PySide6.QtCore import QByteArray, QSize, Qt 

6from PySide6.QtGui import QIcon, QPixmap, QPainter 

7from PySide6.QtSvg import QSvgRenderer 

8 

9from graphica.gui.resources import resource_path 

10 

11ICONS_DIR = os.path.join("assets", "icons") 

12 

13 

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) 

22 

23 

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() 

28 

29 

30def load_svg_icon(svg_path, color="#4B5157", size=24): 

31 """ファイルが無ければ空の QIcon(アイコンが無くてもアプリを落とさない)。""" 

32 if not os.path.exists(svg_path): 

33 return QIcon() 

34 

35 svg_text = _load_svg_text(svg_path).replace("currentColor", color) 

36 renderer = QSvgRenderer(QByteArray(svg_text.encode("utf-8"))) 

37 

38 pixmap = QPixmap(QSize(size, size)) 

39 pixmap.fill(Qt.GlobalColor.transparent) 

40 painter = QPainter(pixmap) 

41 renderer.render(painter) 

42 painter.end() 

43 

44 return QIcon(pixmap)