Files
ctbjrj/tools/make_ribbon_icons.py
T

267 lines
15 KiB
Python

# -*- coding: utf-8 -*-
"""功能区 24 个按钮图标生成器。
SVG 设计稿(语义化,统一风格) → svglib 栅格化 64x64 透明 PNG → bin\Debug\icons\。
幂等可重跑;svg 定义即源档(同步存一份到 icons/svg 供后续编辑)。
风格规范:viewBox 0 0 64 64;主色 #4A4A4A;强调绿 #217346(=功能区 AccentColor);
删除红 #D83931;母排铜 #C8791E;线宽 4 圆角;不用白色填充(保透明底)。"""
import os
import io
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
SVG_DIR = os.path.join(ROOT, "成套报价软件", "icons", "svg")
PNG_DIR = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons")
G = "#4A4A4A" # 主体深灰
GR = "#217346" # 强调绿
RD = "#D83931" # 删除红
CU = "#C8791E" # 铜排橙
BL = "#2B579A" # 文档蓝(点缀)
W = 'stroke="%s" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"' % G
def svg(body):
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">\n' + body + "\n</svg>\n")
# ---- 图标定义: name -> SVG body (64x64) ----
ICONS = {}
# 文档底形(多图标复用): 纸张
def doc(x=14, y=8, w=30, h=44, fold=True, color=G):
f = ('<path d="M%d %d h%d l%d %d v%d h-%d z" %s stroke="%s"/>' %
(x, y, w - 10, 10, 10, h - 10, w, W, color)) if fold else \
('<rect x="%d" y="%d" width="%d" height="%d" rx="3" %s stroke="%s"/>' % (x, y, w, h, W, color))
return f
# 1 button1 新建项目: 文档 + 绿色加号
ICONS["new-project"] = svg(
doc() +
'<line x1="18" y1="24" x2="34" y2="24" %s stroke="%s"/>' % (W, G) +
'<line x1="18" y1="32" x2="30" y2="32" %s stroke="%s"/>' % (W, G) +
'<circle cx="46" cy="44" r="13" fill="%s"/>' % GR +
'<line x1="46" y1="37" x2="46" y2="51" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>' +
'<line x1="39" y1="44" x2="53" y2="44" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>')
# 2 button2 修改项目: 文档 + 笔(斜置)
ICONS["edit-project"] = svg(
doc() +
'<line x1="18" y1="20" x2="28" y2="20" %s stroke="%s"/>' % (W, G) +
'<line x1="18" y1="28" x2="26" y2="28" %s stroke="%s"/>' % (W, G) +
'<path d="M36 46 L52 30 L58 36 L42 52 L34 54 Z" fill="%s" stroke="%s" stroke-width="2"/>' % (CU, "#9C5A10") +
'<line x1="49" y1="33" x2="55" y2="39" stroke="#9C5A10" stroke-width="2"/>')
# 3 button3 删除项目: 垃圾桶
ICONS["delete-project"] = svg(
'<path d="M16 20 h32 l-3 36 a4 4 0 0 1 -4 4 h-18 a4 4 0 0 1 -4 -4 z" %s stroke="%s"/>' % (W, RD) +
'<line x1="12" y1="20" x2="52" y2="20" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % RD +
'<line x1="26" y1="20" x2="26" y2="14" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD +
'<line x1="38" y1="20" x2="38" y2="14" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD +
'<line x1="26" y1="30" x2="27" y2="50" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD +
'<line x1="38" y1="30" x2="37" y2="50" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD)
# 4 button4 打开项目(项目页): 打开的文件夹
ICONS["open-project"] = svg(
'<path d="M8 16 h14 l5 6 h21 v8 h-40 z" %s stroke="%s"/>' % (W, G) +
'<path d="M10 30 h46 l-6 20 h-36 z" %s stroke="%s"/>' % (W, GR) +
'<line x1="18" y1="38" x2="44" y2="38" stroke="%s" stroke-width="3" stroke-linecap="round" opacity="0.55"/>' % GR)
# 5 button5 复制项目: 双层文档
ICONS["copy-project"] = svg(
'<rect x="22" y="8" width="28" height="38" rx="3" %s stroke="%s"/>' % (W, G) +
'<path d="M14 18 v36 h28" %s stroke="%s"/>' % (W, GR) +
'<line x1="29" y1="18" x2="43" y2="18" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % G +
'<line x1="29" y1="26" x2="43" y2="26" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % G +
'<line x1="29" y1="34" x2="38" y2="34" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % G)
# 6 button8 导入项目: 容器+向下箭头(进箱)
ICONS["import-project"] = svg(
'<path d="M10 36 h44 v14 a6 6 0 0 1 -6 6 h-32 a6 6 0 0 1 -6 -6 z" %s stroke="%s"/>' % (W, GR) +
'<line x1="32" y1="6" x2="32" y2="26" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR +
'<path d="M24 20 l8 8 l8 -8" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
# 7 button7 导出项目: 容器+向上箭头(出箱)
ICONS["export-project"] = svg(
'<path d="M10 36 h44 v14 a6 6 0 0 1 -6 6 h-32 a6 6 0 0 1 -6 -6 z" %s stroke="%s"/>' % (W, BL) +
'<line x1="32" y1="26" x2="32" y2="6" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % BL +
'<path d="M24 12 l8 -8 l8 8" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % BL)
# 8 button9 打开项目(报价页): 文件夹+文档探出
ICONS["open-quote"] = svg(
'<path d="M8 14 h14 l5 6 h21 v8 h-40 z" %s stroke="%s"/>' % (W, G) +
'<rect x="26" y="10" width="18" height="24" rx="2" fill="%s" stroke="%s" stroke-width="3"/>' % ("#E8F2EC", GR) +
'<line x1="30" y1="17" x2="40" y2="17" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % GR +
'<line x1="30" y1="24" x2="36" y2="24" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % GR +
'<path d="M10 28 h46 l-6 20 h-36 z" %s stroke="%s"/>' % (W, G))
# 9 button10 修改项目(报价页): 文档+笔+绿点(区分项目页)
ICONS["edit-quote"] = svg(
doc() +
'<line x1="18" y1="20" x2="28" y2="20" %s stroke="%s"/>' % (W, G) +
'<circle cx="24" cy="46" r="11" fill="%s"/>' % GR +
'<path d="M34 42 L48 28 L54 34 L40 48 L33 50 Z" fill="%s" stroke="%s" stroke-width="2"/>' % (CU, "#9C5A10"))
# 10 button17 保存: 软盘(绿色)
ICONS["save"] = svg(
'<path d="M10 10 h36 l8 8 v36 h-44 z" %s stroke="%s"/>' % (W, GR) +
'<rect x="20" y="10" width="20" height="14" %s stroke="%s"/>' % (W, GR) +
'<rect x="20" y="36" width="24" height="18" %s stroke="%s"/>' % (W, GR) +
'<line x1="38" y1="12" x2="38" y2="20" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % GR)
# 11 button18 导入报价: 文档+向下箭头
ICONS["import-quote"] = svg(
'<rect x="10" y="8" width="30" height="44" rx="3" %s stroke="%s"/>' % (W, G) +
'<line x1="17" y1="18" x2="33" y2="18" %s stroke="%s"/>' % (W, G) +
'<line x1="17" y1="26" x2="29" y2="26" %s stroke="%s"/>' % (W, G) +
'<line x1="48" y1="30" x2="48" y2="54" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR +
'<path d="M40 48 l8 8 l8 -8" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
# 12 button16 甲方报表: 表格+绿色对勾
ICONS["report"] = svg(
'<rect x="8" y="10" width="48" height="44" rx="3" %s stroke="%s"/>' % (W, G) +
'<line x1="8" y1="22" x2="56" y2="22" %s stroke="%s"/>' % (W, G) +
'<line x1="8" y1="34" x2="56" y2="34" %s stroke="%s"/>' % (W, G) +
'<line x1="24" y1="22" x2="24" y2="54" %s stroke="%s"/>' % (W, G) +
'<line x1="40" y1="22" x2="40" y2="54" %s stroke="%s"/>' % (W, G) +
'<circle cx="47" cy="45" r="12" fill="%s"/>' % GR +
'<path d="M41 45 l5 5 l8 -9" stroke="#FFFFFF" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>')
# 13 button15 调母排: 三根铜排+调节箭头
ICONS["busbar"] = svg(
'<rect x="8" y="14" width="48" height="8" rx="3" fill="%s"/>' % CU +
'<rect x="8" y="30" width="48" height="8" rx="3" fill="%s" opacity="0.85"/>' % CU +
'<rect x="8" y="46" width="34" height="8" rx="3" fill="%s" opacity="0.7"/>' % CU +
'<path d="M46 56 l6 -6 l6 6" stroke="%s" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % G)
# 14 button13 调系数: 双滑块调节
ICONS["factor"] = svg(
'<line x1="12" y1="22" x2="52" y2="22" %s stroke="%s"/>' % (W, G) +
'<line x1="12" y1="42" x2="52" y2="42" %s stroke="%s"/>' % (W, G) +
'<circle cx="40" cy="22" r="7" fill="%s" stroke="%s" stroke-width="3"/>' % (GR, GR) +
'<circle cx="24" cy="42" r="7" fill="%s" stroke="%s" stroke-width="3"/>' % (BL, BL))
# 15 button33 元件排序: 列表+上下箭头
ICONS["sort-components"] = svg(
'<circle cx="16" cy="16" r="4" fill="%s"/>' % G +
'<circle cx="16" cy="32" r="4" fill="%s"/>' % G +
'<circle cx="16" cy="48" r="4" fill="%s"/>' % G +
'<line x1="26" y1="16" x2="44" y2="16" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
'<line x1="26" y1="32" x2="40" y2="32" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
'<line x1="26" y1="48" x2="36" y2="48" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
'<path d="M50 20 l6 -6 l6 6" stroke="%s" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR +
'<path d="M50 44 l6 6 l6 -6" stroke="%s" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
# 16 button29 模板费用: 模板网格+¥
ICONS["template-cost"] = svg(
'<rect x="8" y="8" width="48" height="48" rx="4" %s stroke="%s"/>' % (W, G) +
'<line x1="8" y1="24" x2="56" y2="24" %s stroke="%s"/>' % (W, G) +
'<line x1="8" y1="40" x2="36" y2="40" %s stroke="%s"/>' % (W, G) +
'<line x1="30" y1="24" x2="30" y2="56" %s stroke="%s"/>' % (W, G) +
'<circle cx="47" cy="45" r="13" fill="%s"/>' % GR +
'<text x="47" y="53" font-family="Segoe UI, Arial" font-size="20" font-weight="bold" fill="#FFFFFF" text-anchor="middle">¥</text>')
# 17 button11 CAD扒图: 齿轮+放大镜
ICONS["cad-grab"] = svg(
'<circle cx="26" cy="26" r="9" %s stroke="%s"/>' % (W, G) +
'<path d="M26 8 v6 M26 38 v6 M8 26 h6 M38 26 h6 M13 13 l4.5 4.5 M34.5 34.5 L39 39 M39 13 l-4.5 4.5 M17.5 34.5 L13 39" '
'stroke="%s" stroke-width="5" stroke-linecap="round"/>' % G +
'<circle cx="44" cy="44" r="10" %s stroke="%s"/>' % (W, GR) +
'<line x1="51" y1="51" x2="58" y2="58" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR)
# 18 btn登录: 人形+门框
ICONS["login"] = svg(
'<path d="M40 8 h14 v48 h-14" %s stroke="%s"/>' % (W, G) +
'<circle cx="22" cy="24" r="8" %s stroke="%s"/>' % (W, GR) +
'<path d="M8 52 a14 14 0 0 1 28 0" %s stroke="%s"/>' % (W, GR) +
'<line x1="48" y1="32" x2="48" y2="36" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G)
# 19 btn修改密码: 锁+钥匙孔
ICONS["password"] = svg(
'<rect x="14" y="28" width="36" height="28" rx="4" %s stroke="%s"/>' % (W, GR) +
'<path d="M22 28 v-8 a10 10 0 0 1 20 0 v8" %s stroke="%s"/>' % (W, GR) +
'<circle cx="32" cy="40" r="4" fill="%s"/>' % GR +
'<line x1="32" y1="44" x2="32" y2="50" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % GR)
# 20 btn会员信息: 人像卡片
ICONS["member"] = svg(
'<rect x="8" y="14" width="48" height="36" rx="4" %s stroke="%s"/>' % (W, G) +
'<circle cx="24" cy="30" r="6" %s stroke="%s"/>' % (W, GR) +
'<path d="M14 46 a10 10 0 0 1 20 0" %s stroke="%s"/>' % (W, GR) +
'<line x1="40" y1="26" x2="50" y2="26" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
'<line x1="40" y1="36" x2="50" y2="36" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G)
# 21 btn企业信息: 办公楼
ICONS["company"] = svg(
'<rect x="12" y="10" width="28" height="46" %s stroke="%s"/>' % (W, G) +
'<rect x="40" y="26" width="14" height="30" %s stroke="%s"/>' % (W, GR) +
'<line x1="19" y1="20" x2="25" y2="20" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
'<line x1="31" y1="20" x2="37" y2="20" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
'<line x1="19" y1="30" x2="25" y2="30" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
'<line x1="31" y1="30" x2="37" y2="30" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
'<line x1="19" y1="40" x2="25" y2="40" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
'<line x1="31" y1="40" x2="37" y2="40" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
'<rect x="22" y="48" width="8" height="8" %s stroke="%s"/>' % (W, G))
# 22 btn保存布局: 网格布局+绿色对勾
ICONS["save-layout"] = svg(
'<rect x="8" y="8" width="48" height="48" rx="4" %s stroke="%s"/>' % (W, G) +
'<line x1="8" y1="26" x2="56" y2="26" %s stroke="%s"/>' % (W, G) +
'<line x1="30" y1="26" x2="30" y2="56" %s stroke="%s"/>' % (W, G) +
'<rect x="12" y="12" width="14" height="10" fill="%s" opacity="0.85"/>' % GR +
'<circle cx="45" cy="43" r="12" fill="%s"/>' % GR +
'<path d="M39 43 l5 5 l8 -9" stroke="#FFFFFF" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>')
# 23 btn备份: 数据库+向右箭头
ICONS["backup"] = svg(
'<ellipse cx="24" cy="16" rx="14" ry="6" %s stroke="%s"/>' % (W, G) +
'<path d="M10 16 v30 a14 6 0 0 0 28 0 v-30" %s stroke="%s"/>' % (W, G) +
'<path d="M10 31 a14 6 0 0 0 28 0" %s stroke="%s"/>' % (W, G) +
'<line x1="40" y1="40" x2="56" y2="40" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR +
'<path d="M50 34 l8 6 l-8 6" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
# 24 btn恢复: 数据库+向左回箭头
ICONS["restore"] = svg(
'<ellipse cx="40" cy="16" rx="14" ry="6" %s stroke="%s"/>' % (W, GR) +
'<path d="M26 16 v30 a14 6 0 0 0 28 0 v-30" %s stroke="%s"/>' % (W, GR) +
'<path d="M26 31 a14 6 0 0 0 28 0" %s stroke="%s"/>' % (W, GR) +
'<line x1="24" y1="40" x2="8" y2="40" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % G +
'<path d="M14 34 l-8 6 l8 6" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % G)
# 按钮 Name → 图标文件映射(与 Designer.cs 集合一致)
BIND = {
"button1": "new-project", "button2": "edit-project", "button3": "delete-project",
"button4": "open-project", "button5": "copy-project", "button7": "export-project",
"button8": "import-project", "button9": "open-quote", "button10": "edit-quote",
"button17": "save", "button18": "import-quote", "button16": "report",
"button15": "busbar", "button13": "factor", "button33": "sort-components",
"button29": "template-cost", "button11": "cad-grab",
"btn登录": "login", "btn修改密码": "password", "btn会员信息": "member",
"btn企业信息": "company", "btn保存布局": "save-layout",
"btn备份": "backup", "btn恢复": "restore",
}
def render_all():
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
os.makedirs(SVG_DIR, exist_ok=True)
os.makedirs(PNG_DIR, exist_ok=True)
ok, fail = 0, []
for name, body in ICONS.items():
sp = os.path.join(SVG_DIR, name + ".svg")
with io.open(sp, "w", encoding="utf-8") as f:
f.write(svg(body) if not body.startswith("<svg") else body)
try:
d = svg2rlg(sp)
# 64x64 输出(svglib 默认按 72dpi 走 1:1,viewBox 64 → 64px)
pp = os.path.join(PNG_DIR, name + ".png")
renderPM.drawToFile(d, pp, fmt="PNG", dpi=72, bg=0x00000000)
ok += 1
except Exception as ex:
fail.append((name, str(ex)[:120]))
print("PNG 生成: %d 成功 / %d 失败, 输出 %s" % (ok, len(fail), PNG_DIR))
for n, e in fail:
print(" [失败]", n, e)
if __name__ == "__main__":
render_all()