"""根图(第 1 页):左栏层次页符号 + 右侧内嵌 drawio 框图 PNG。 跨页连接一律用全局标签,层次页不需要 sheet pin。子页的 sheet_uuid 由 uid("sh", 文件名) 推出,与 page.Page 的约定一致,所以根图和子图可以各自单独重生成。 from schgen.root import write_root write_root(prj, [("02_Power.kicad_sch", "02 电源"), ...], png=r"...\\diagram.png", title="MyBoard", note="一段说明文字") 插图:KiCad 对没有 pHYs 的 PNG 按 300DPI 解释,scale=1 时宽 = 像素/300*25.4mm(G-0022), 据此反算 scale;(image (at 中心)) 给的是中心不是左上角;base64 每行 76 字符。 """ import base64 import io import os import struct from .kicad_lib import uid, esc from .layout import PAPER KICAD_BITMAP_DPI = 300.0 def image_block(png, x0, y0, max_w, max_h, key="img"): raw = io.open(png, "rb").read() px, py = struct.unpack(">II", raw[16:24]) nat_w, nat_h = px / KICAD_BITMAP_DPI * 25.4, py / KICAD_BITMAP_DPI * 25.4 scale = min(max_w / nat_w, max_h / nat_h) w, h = nat_w * scale, nat_h * scale cx, cy = x0 + w / 2.0, y0 + h / 2.0 b64 = base64.b64encode(raw).decode("ascii") body = "\n\t\t\t".join('"%s"' % b64[i:i + 76] for i in range(0, len(b64), 76)) blk = ('\t(image\n\t\t(at %.3f %.3f)\n\t\t(scale %.6f)\n\t\t(uuid "%s")\n' '\t\t(data %s\n\t\t)\n\t)' % (cx, cy, scale, uid("root", key, os.path.basename(png)), body)) # 只用文件名:工程搬目录后重生成结果不变 return blk, (x0, y0, w, h) def write_root(prj, sheets, png=None, title=None, note="", paper="A2", col_w=110.0, sheet_h=18.0, gap=10.0, img_note=None): """sheets: [(文件名, 显示名), ...],按顺序排在左栏(页码 2、3…)。""" PW, PH = PAPER[paper] title = title or prj.name root_u = uid("root", prj.name) out = ['(kicad_sch (version 20231120) (generator "kicad_flow_schgen") (generator_version "8.0")', ' (uuid "%s")' % root_u, ' (paper "%s")' % paper, ' (title_block (title "%s") (rev "%s") (company "%s"))' % (esc(title), prj.rev, esc(prj.company)), ' (lib_symbols)'] x = 26.0 y = 24.0 if note: out.append(' (text "%s" (at %.1f %.1f 0) (effects (font (size 1.8 1.8)) (justify left top))' ' (uuid "%s"))' % (esc(note), x, y, uid("root", "note"))) y += 6.0 + 2.6 * (note.count("\n") + 1) for i, (fn, name) in enumerate(sheets): sy = y + 6.0 + i * (sheet_h + gap) out.append( ' (sheet (at %.1f %.1f) (size %.1f %.1f) (fields_autoplaced yes)\n' ' (stroke (width 0.1524) (type solid)) (fill (color 0 0 0 0.0))\n' ' (uuid "%s")\n' ' (property "Sheetname" "%s" (at %.1f %.2f 0)\n' ' (effects (font (size 1.8 1.8)) (justify left bottom)))\n' ' (property "Sheetfile" "%s" (at %.1f %.2f 0)\n' ' (effects (font (size 1.4 1.4)) (justify left top)))\n' ' (instances (project "%s" (path "/%s" (page "%d"))))\n' ' )' % (x, sy, col_w, sheet_h, uid("sh", fn), esc(name), x, sy - 1.0, fn, x, sy + sheet_h + 1.0, prj.name, root_u, i + 2)) info = None if png: ix0 = x + col_w + 14.0 iy0 = 24.0 blk, info = image_block(png, ix0, iy0, PW - 14.0 - ix0, PH - 40.0 - iy0 - 8.0) out.append(blk) if img_note: out.append(' (text "%s" (at %.1f %.1f 0) (effects (font (size 1.6 1.6)) (justify left top))' ' (uuid "%s"))' % (esc(img_note), ix0, iy0 + info[3] + 3.0, uid("root", "imgnote"))) out.append(' (sheet_instances (path "/" (page "1")))') out.append(')') p = os.path.join(prj.out_dir, prj.name + ".kicad_sch") io.open(p, "w", encoding="utf-8").write("\n".join(out) + "\n") print("根图 %s (%.0f KB)%s" % (p, os.path.getsize(p) / 1024.0, " 框图 %.0fx%.0f mm" % (info[2], info[3]) if info else "")) return p def write_project(prj): """最小 .kicad_pro(没有它 KiCad 打不开工程);已存在就不动。""" p = os.path.join(prj.out_dir, prj.name + ".kicad_pro") if not os.path.exists(p): io.open(p, "w", encoding="utf-8").write( '{\n "meta": {"filename": "%s.kicad_pro", "version": 1},\n' ' "schematic": {"legacy_lib_dir": "", "legacy_lib_list": []},\n' ' "sheets": [],\n "boards": [],\n "libraries": {"pinned_footprint_libs": [],' ' "pinned_symbol_libs": []}\n}\n' % prj.name) return p def write_lib_tables(prj, sym_libs=(), fp_libs=()): """工程级 sym-lib-table / fp-lib-table,用 ${KIPRJMOD} 相对路径。 改完必须**整个退出 KiCad 再重启**才生效(G-0020)。""" def table(kind, libs): rows = ['(%s_lib_table' % kind, ' (version 7)'] for name, rel in libs: # 绝对路径原样写;ipots 工程里 ${KIPRJMOD} 版 fp-lib-table 曾没被认到(多半是没重启) uri = rel if os.path.isabs(rel) else "${KIPRJMOD}/" + rel rows.append(' (lib (name "%s") (type "KiCad") (uri "%s") (options "")' ' (descr ""))' % (name, uri.replace("\\", "/"))) rows.append(')') return "\n".join(rows) + "\n" if sym_libs: io.open(os.path.join(prj.out_dir, "sym-lib-table"), "w", encoding="utf-8").write( table("sym", sym_libs)) if fp_libs: io.open(os.path.join(prj.out_dir, "fp-lib-table"), "w", encoding="utf-8").write( table("fp", fp_libs))