"""모노크롬 라인 아이콘 (Lucide 스타일) — 테마 색으로 틴팅한 QIcon 생성. 이모지를 대체하는 세련된 벡터 아이콘. QtSvg로 24x24 stroke path를 렌더링하고 (name, color, size)별로 캐시. 색은 호출 시점의 테마 색을 받으므로 테마 전환 시 재호출하면 자동으로 재틴팅된다. 사용: from ui.icons import get_icon btn.setIcon(get_icon('settings')) # 기본: text_secondary 색 btn.setIcon(get_icon('logout', '#FFFFFF')) # 색 지정 """ from __future__ import annotations from PyQt5.QtCore import QByteArray, QRectF, Qt from PyQt5.QtGui import QIcon, QPixmap, QPainter from PyQt5.QtSvg import QSvgRenderer from ui.styles import ThemeColors # 24x24 viewBox 기준 내부 path 마크업 (Lucide). stroke 기반, fill 없음. _PATHS = { 'chart': '', 'calendar': '', 'report': '', 'award': '', 'help': '', 'settings': '', 'logout': '', 'rotate-ccw': '', 'edit': '', 'clock': '', 'trash': '', 'flame': '', 'trending-up': '', 'search': '', 'external-link': '', 'coffee': '', 'repeat': '', 'home': '', } _SVG_TMPL = ( '{paths}' ) _cache: dict = {} def get_icon(name: str, color: str = None, size: int = 18) -> QIcon: """이름·색·크기로 틴팅된 QIcon 반환 (캐시됨). 미정의 이름은 빈 QIcon.""" if color is None: color = ThemeColors.get('text_secondary') key = (name, color, size) cached = _cache.get(key) if cached is not None: return cached paths = _PATHS.get(name) if paths is None: return QIcon() svg = _SVG_TMPL.format(color=color, paths=paths).encode('utf-8') renderer = QSvgRenderer(QByteArray(svg)) dpr = 2 # 2x 렌더 후 devicePixelRatio 지정 → HiDPI에서도 선명 pm = QPixmap(size * dpr, size * dpr) pm.fill(Qt.transparent) painter = QPainter(pm) renderer.render(painter, QRectF(0, 0, size * dpr, size * dpr)) painter.end() pm.setDevicePixelRatio(dpr) icon = QIcon(pm) _cache[key] = icon return icon def clear_cache() -> None: """테마 전환 등으로 캐시를 비울 때 사용 (보통은 키가 색을 포함하므로 불필요).""" _cache.clear()