# -*- coding: utf-8 -*- """ui_layout_image 스킬용 렌더 헬퍼 (dark HMI 스타일). 사용 예: PYTHONUTF8=1 py .claude/skills/ui_layout_image/render_helpers.py # 데모 1장 생성 (셸 cwd 는 호출마다 초기화되므로 맨 파일명으로 실행하지 않는다) 실전에서는 import 하여 panel/kv/button/label 로 화면별 도식을 그린다. """ import os import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.patches import FancyBboxPatch, Rectangle import matplotlib.font_manager as fm # ---- 한글 폰트 ---- FONT = r"C:\Windows\Fonts\malgun.ttf" try: fp = fm.FontProperties(fname=FONT) plt.rcParams["font.family"] = fp.get_name() except Exception: pass plt.rcParams["axes.unicode_minus"] = False # ---- dark HMI 팔레트 (프로젝트 UI와 일치) ---- BG = "#2D2D30"; PANEL = "#3E3E42"; BORDER = "#6E6E72"; HEADER = "#007ACC" TXT = "#DDDDDD"; PURPLE = "#7E57C2"; BLUE = "#2D6CA2"; GREEN = "#2E7D32"; GRAY = "#555558" INPUT = "#252528"; VALCOLOR = "#9CDCFE" def new_canvas(w, h): fig = plt.figure(figsize=(w / 100, h / 100), dpi=100) ax = fig.add_axes([0, 0, 1, 1]) ax.set_xlim(0, w); ax.set_ylim(h, 0); ax.axis("off") ax.add_patch(Rectangle((0, 0), w, h, facecolor=BG, edgecolor="none")) return fig, ax def panel(ax, x, y, w, h, header=None, fc=PANEL): ax.add_patch(FancyBboxPatch((x + 2, y + 2), w - 4, h - 4, boxstyle="round,pad=2", facecolor=fc, edgecolor=BORDER, linewidth=1.2)) if header: ax.add_patch(Rectangle((x + 4, y + 4), w - 8, 22, facecolor=HEADER, edgecolor="none")) ax.text(x + 12, y + 15, header, color="white", fontsize=9, fontweight="bold", va="center") return x + 8, y + 30 def label(ax, x, y, text, size=8.5, color=TXT, bold=False, ha="left"): ax.text(x, y, text, color=color, fontsize=size, va="center", ha=ha, fontweight=("bold" if bold else "normal")) def kv(ax, x, y, w, key, val=""): ax.text(x + w * 0.46, y, key, color=TXT, fontsize=8, va="center", ha="right") ax.add_patch(Rectangle((x + w * 0.5, y - 9), w * 0.48, 18, facecolor=INPUT, edgecolor=BORDER, lw=0.6)) if val: ax.text(x + w * 0.52, y, val, color=VALCOLOR, fontsize=8, va="center", ha="left") def button(ax, x, y, w, h, text, color=BLUE): ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle="round,pad=1.5", facecolor=color, edgecolor="none")) ax.text(x + w / 2, y + h / 2, text, color="white", fontsize=8.5, fontweight="bold", ha="center", va="center") def save(fig, path): d = os.path.dirname(path) if d: # 파일명만 준 경우 dirname 이 '' 이라 makedirs 가 FileNotFoundError 를 낸다 os.makedirs(d, exist_ok=True) fig.savefig(path, facecolor=BG) plt.close(fig) if __name__ == "__main__": fig, ax = new_canvas(600, 300) panel(ax, 10, 10, 580, 280, "Demo Panel") kv(ax, 30, 60, 300, "Key", "Value") button(ax, 30, 90, 120, 28, "Run", PURPLE) # 출력은 스킬 폴더가 아니라 SKILL.md 가 지정한 로컬 산출 폴더로 보낸다. repo = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) out = os.path.join(repo, "UnTrackedLocalFiles", "hmi_mockups", "_demo.png") save(fig, out) print("demo saved:", out)