# -*- coding: utf-8 -*- """功能区 24 个按钮图标:Pillow 等价绘制(本机无 cairo,SVG 无法栅格化—— icons/svg/*.svg 为设计稿存档,本脚本按同一几何画 256x256 超采样 → 64x64 透明 PNG)。 输出 bin\\Debug\\icons\\*.png,文件名与 make_ribbon_icons.py 的 BIND 映射一致。""" import os from PIL import Image, ImageDraw, ImageFont ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件" OUT = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons") SVG_DIR = os.path.join(ROOT, "成套报价软件", "icons", "svg") S = 4 # 256 = 64*4 超采样 G = (74, 74, 74, 255) # 主体深灰 GR = (33, 115, 70, 255) # 强调绿 RD = (216, 57, 49, 255) # 删除红 CU = (200, 121, 30, 255) # 铜排橙 BL = (43, 87, 154, 255) # 点缀蓝 WH = (255, 255, 255, 255) PGL = (232, 242, 236, 255) # 淡绿底 def L(v): # 64 坐标 → 256(取整,Pillow 不收浮点) return int(round(v * S)) def line(d, pts, color, w=4): d.line([(L(x), L(y)) for x, y in pts], fill=color, width=L(w), joint="curve") def rrect(d, x, y, w2, h2, r, color, width=4, fill=None): d.rounded_rectangle([L(x), L(y), L(x + w2), L(y + h2)], radius=L(r), outline=color, width=L(width), fill=fill) def rect(d, x, y, w2, h2, color, width=4, fill=None): d.rectangle([L(x), L(y), L(x + w2), L(y + h2)], outline=color, width=L(width), fill=fill) def circle(d, cx, cy, r, color=None, width=4, fill=None): d.ellipse([L(cx - r), L(cy - r), L(cx + r), L(cy + r)], outline=color, width=L(width) if color else 0, fill=fill) def ell(d, cx, cy, rx, ry, color, width=4, fill=None): d.ellipse([L(cx - rx), L(cy - ry), L(cx + rx), L(cy + ry)], outline=color, width=L(width), fill=fill) def arc(d, cx, cy, r, a1, a2, color, width=4): d.arc([L(cx - r), L(cy - r), L(cx + r), L(cy + r)], start=a1, end=a2, fill=color, width=L(width)) def doc_outline(d, x=14, y=8, w2=30, h2=44, color=G): """折角文档轮廓""" line(d, [(x, y), (x + w2 - 10, y), (x + w2, y + 10), (x + w2, y + h2), (x, y + h2), (x, y)], color) def poly(d, pts, color, width=4, fill=None, close=True): p = [(L(x), L(y)) for x, y in pts] if fill: d.polygon(p, fill=fill) if close: p = p + [p[0]] d.line(p, fill=color, width=L(width), joint="curve") # ============ 24 个图标(与 icons/svg 设计稿同几何) ============ def i_new_project(d): doc_outline(d) line(d, [(18, 24), (34, 24)], G) line(d, [(18, 32), (30, 32)], G) circle(d, 46, 44, 13, fill=GR) line(d, [(46, 37), (46, 51)], WH) line(d, [(39, 44), (53, 44)], WH) def i_edit_project(d): doc_outline(d) line(d, [(18, 20), (28, 20)], G) line(d, [(18, 28), (26, 28)], G) poly(d, [(36, 46), (52, 30), (58, 36), (42, 52), (34, 54)], (156, 90, 16, 255), width=2, fill=CU) line(d, [(49, 33), (55, 39)], (156, 90, 16, 255), w=2) def i_delete_project(d): poly(d, [(16, 20), (48, 20), (45, 56), (23, 56)], RD, width=4) line(d, [(12, 20), (52, 20)], RD, w=5) line(d, [(26, 20), (26, 14)], RD) line(d, [(38, 20), (38, 14)], RD) line(d, [(26, 30), (27, 50)], RD) line(d, [(38, 30), (37, 50)], RD) def i_open_project(d): poly(d, [(8, 16), (22, 16), (27, 22), (48, 22), (48, 30), (8, 30)], G, width=4) poly(d, [(10, 30), (56, 30), (50, 50), (14, 50)], GR, width=4) line(d, [(18, 38), (44, 38)], (150, 190, 168, 255), w=3) def i_copy_project(d): rrect(d, 22, 8, 28, 38, 3, G) poly(d, [(14, 18), (14, 54), (42, 54)], GR, width=4, close=False) line(d, [(29, 18), (43, 18)], G, w=3) line(d, [(29, 26), (43, 26)], G, w=3) line(d, [(29, 34), (38, 34)], G, w=3) def _box_u(d, color): """开口容器:U 形(左、底、右边,顶部开放)""" line(d, [(10, 36), (10, 50), (16, 56), (48, 56), (54, 50), (54, 36)], color, w=4) def i_import_project(d): _box_u(d, GR) line(d, [(32, 6), (32, 26)], GR, w=5) poly(d, [(24, 20), (32, 28), (40, 20)], GR, width=5, close=False) def i_export_project(d): _box_u(d, BL) line(d, [(32, 26), (32, 6)], BL, w=5) poly(d, [(24, 12), (32, 4), (40, 12)], BL, width=5, close=False) def i_open_quote(d): poly(d, [(8, 14), (22, 14), (27, 20), (48, 20), (48, 28), (8, 28)], G, width=4) rrect(d, 26, 10, 18, 24, 2, GR, width=3, fill=PGL) line(d, [(30, 17), (40, 17)], GR, w=3) line(d, [(30, 24), (36, 24)], GR, w=3) poly(d, [(10, 28), (56, 28), (50, 48), (14, 48)], G, width=4) def i_edit_quote(d): doc_outline(d) line(d, [(18, 20), (28, 20)], G) circle(d, 24, 46, 11, fill=GR) poly(d, [(34, 42), (48, 28), (54, 34), (40, 48), (33, 50)], (156, 90, 16, 255), width=2, fill=CU) def i_save(d): poly(d, [(10, 10), (46, 10), (54, 18), (54, 54), (10, 54)], GR, width=4) rect(d, 20, 10, 20, 14, GR, width=4) rect(d, 20, 36, 24, 18, GR, width=4) line(d, [(38, 12), (38, 20)], GR) def i_import_quote(d): rrect(d, 10, 8, 30, 44, 3, G) line(d, [(17, 18), (33, 18)], G) line(d, [(17, 26), (29, 26)], G) line(d, [(48, 30), (48, 54)], GR, w=5) poly(d, [(40, 48), (48, 56), (56, 48)], GR, width=5, close=False) def i_report(d): rrect(d, 8, 10, 48, 44, 3, G) line(d, [(8, 22), (56, 22)], G) line(d, [(8, 34), (56, 34)], G) line(d, [(24, 22), (24, 54)], G) line(d, [(40, 22), (40, 54)], G) circle(d, 47, 45, 12, fill=GR) poly(d, [(41, 45), (46, 50), (54, 41)], WH, width=4, close=False) def i_busbar(d): # 三根铜排(fill 直接画,深浅表现层次)+右下调节箭头 d.rounded_rectangle([L(8), L(14), L(56), L(22)], radius=L(3), fill=CU) d.rounded_rectangle([L(8), L(30), L(56), L(38)], radius=L(3), fill=(214, 149, 74, 255)) d.rounded_rectangle([L(8), L(46), L(42), L(54)], radius=L(3), fill=(227, 177, 118, 255)) poly(d, [(48, 58), (54, 52), (60, 58)], G, width=4, close=False) def i_factor(d): line(d, [(12, 22), (52, 22)], G) line(d, [(12, 42), (52, 42)], G) circle(d, 40, 22, 7, GR, width=3, fill=GR) circle(d, 24, 42, 7, BL, width=3, fill=BL) def i_sort_components(d): circle(d, 16, 16, 4, fill=G) circle(d, 16, 32, 4, fill=G) circle(d, 16, 48, 4, fill=G) line(d, [(26, 16), (44, 16)], G) line(d, [(26, 32), (40, 32)], G) line(d, [(26, 48), (36, 48)], G) poly(d, [(50, 20), (56, 14), (62, 20)], GR, width=4, close=False) poly(d, [(50, 44), (56, 50), (62, 44)], GR, width=4, close=False) def i_template_cost(d): rrect(d, 8, 8, 48, 48, 4, G) line(d, [(8, 24), (56, 24)], G) line(d, [(8, 40), (36, 40)], G) line(d, [(30, 24), (30, 56)], G) circle(d, 47, 45, 13, fill=GR) try: font = ImageFont.truetype("msyhbd.ttc", L(22)) except Exception: font = ImageFont.truetype("arialbd.ttf", L(22)) d.text((L(47), L(45)), "¥", font=font, fill=WH, anchor="mm") def i_cad_grab(d): circle(d, 26, 26, 9, G) for pts in [[(26, 8), (26, 14)], [(26, 38), (26, 44)], [(8, 26), (14, 26)], [(38, 26), (44, 26)], [(13, 13), (17.5, 17.5)], [(34.5, 34.5), (39, 39)], [(39, 13), (34.5, 17.5)], [(17.5, 34.5), (13, 39)]]: line(d, pts, G, w=5) circle(d, 44, 44, 10, GR) line(d, [(51, 51), (58, 58)], GR, w=5) def i_login(d): poly(d, [(40, 8), (54, 8), (54, 56), (40, 56)], G, width=4, close=False) circle(d, 22, 24, 8, GR) arc(d, 22, 48, 14, 180, 360, GR) line(d, [(48, 32), (48, 36)], G) def i_password(d): rrect(d, 14, 28, 36, 28, 4, GR) arc(d, 32, 28, 10, 180, 360, GR) line(d, [(22, 28), (22, 20)], GR) line(d, [(42, 28), (42, 20)], GR) circle(d, 32, 40, 4, fill=GR) line(d, [(32, 44), (32, 50)], GR) def i_member(d): rrect(d, 8, 14, 48, 36, 4, G) circle(d, 24, 30, 6, GR) arc(d, 24, 43, 10, 180, 360, GR) line(d, [(40, 26), (50, 26)], G) line(d, [(40, 36), (50, 36)], G) def i_company(d): rect(d, 12, 10, 28, 46, G) rect(d, 40, 26, 14, 30, GR) for y in (20, 30, 40): line(d, [(19, y), (25, y)], G, w=3.5) line(d, [(31, y), (37, y)], G, w=3.5) rect(d, 22, 48, 8, 8, G) def i_save_layout(d): rrect(d, 8, 8, 48, 48, 4, G) line(d, [(8, 26), (56, 26)], G) line(d, [(30, 26), (30, 56)], G) d.rectangle([L(12), L(12), L(26), L(22)], fill=(150, 190, 168, 255)) circle(d, 45, 43, 12, fill=GR) poly(d, [(39, 43), (44, 48), (52, 39)], WH, width=4, close=False) def _db(d, cx, color): ell(d, cx, 16, 14, 6, color) line(d, [(cx - 14, 16), (cx - 14, 46)], color) line(d, [(cx + 14, 16), (cx + 14, 46)], color) arc(d, cx, 40, 14, 0, 180, color) arc(d, cx, 25, 14, 0, 180, color) def i_backup(d): _db(d, 24, G) line(d, [(40, 40), (56, 40)], GR, w=5) poly(d, [(50, 34), (58, 40), (50, 46)], GR, width=5, close=False) def i_restore(d): _db(d, 40, GR) line(d, [(24, 40), (8, 40)], G, w=5) poly(d, [(14, 34), (6, 40), (14, 46)], G, width=5, close=False) ICONS = { "new-project": i_new_project, "edit-project": i_edit_project, "delete-project": i_delete_project, "open-project": i_open_project, "copy-project": i_copy_project, "export-project": i_export_project, "import-project": i_import_project, "open-quote": i_open_quote, "edit-quote": i_edit_quote, "save": i_save, "import-quote": i_import_quote, "report": i_report, "busbar": i_busbar, "factor": i_factor, "sort-components": i_sort_components, "template-cost": i_template_cost, "cad-grab": i_cad_grab, "login": i_login, "password": i_password, "member": i_member, "company": i_company, "save-layout": i_save_layout, "backup": i_backup, "restore": i_restore, } def main(): os.makedirs(OUT, exist_ok=True) ok = 0 for name, fn in ICONS.items(): img = Image.new("RGBA", (64 * S, 64 * S), (0, 0, 0, 0)) d = ImageDraw.Draw(img) fn(d) img = img.resize((64, 64), Image.LANCZOS) img.save(os.path.join(OUT, name + ".png")) ok += 1 print("Pillow 图标生成 %d 个 → %s" % (ok, OUT)) if __name__ == "__main__": main()