118 lines
5.6 KiB
Python
118 lines
5.6 KiB
Python
"""根图(第 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))
|