ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

手搓EDA软件:原理图编辑器从数据模型到网表导出的完整实现

2026/9/2 5:47:07 拓冰建站 浏览量
手搓EDA软件:原理图编辑器从数据模型到网表导出的完整实现 这次我们来看一个硬核又有点“极客浪漫”的项目手搓 EDA 软件。上一期汇报演出了什么先不管这次聚焦在原理图编辑器上。所谓手搓就是不走成熟开源框架的现成路线从数据结构、图形交互、网络连接、物料清单这些底层逻辑开始自己实现一套能用的原理图编辑核心。如果你关心的是“EDA 工具到底怎么设计”“原理图编辑器里那些连线、元件、网络标号是怎么存进去的”“能不能自己写一个简易版立创EDA”这篇文章可以直接翻到对应章节看。我会按实际开发流程把原理图编辑器的核心模块、环境准备、界面启动、功能验证、数据导出和常见坑位全部过一遍。先说结论这个项目不是给生产打板用的它的价值在于把 EDA 原理图编辑器从“黑盒工具”变成“可拆解的工程系统”。你可以自己在上面扩展元件库、自定义快捷键、接入仿真脚本甚至把编辑好的原理图导出成网表给 PCB 布局模块用。整个项目对显卡没有要求CPU 就能跑重点在数据结构和图形交互设计。1. 核心能力速览能力项说明项目类型自研 EDA 原理图编辑器核心实现主要功能元件放置、导线连接、网络标号、电源符号、属性编辑、网表导出硬件门槛无 GPU 要求普通 CPU 即可2GB 内存以上建议支持平台跨平台桌面应用支持 Windows / Linux / macOS启动方式命令行启动可打包为桌面应用接口能力支持原理图数据文件读写可接仿真模块和 PCB 编辑器批量任务支持批量元件属性检查和网表自动生成适合人群EDA 工具开发者、电子工程专业学生、对工具链底层感兴趣的工程师这个项目的核心不是画图多流畅而是把原理图编辑器拆成了几个标准模块符号库管理、画布交互、电气连接识别、层次化设计、物料清单导出。每一个模块都可以独立替换和扩展这也是它区别于市面现成工具的最大特点。2. 适用场景与使用边界2.1 适合谁用想了解 EDA 工具内部原理的嵌入式工程师。画了这么多元件图终于能知道鼠标拖动元件的背后发生了什么。电子类课程设计需要做工具链开发的学生。可以直接在此基础上加功能不用从零造轮子。对嘉立创 EDA、KiCad 等工具操作很熟但想自己实现一套简化版流程的开发者。做内部自动化设计工具的团队。可以把它当作原理图编辑内核再封装成 Web 版或桌面版。2.2 不适合什么不适合用来画复杂 PCB 工程并直接投板。这个项目更偏演示和二次开发生产级设计请使用嘉立创 EDA 专业版、KiCad、Altium Designer 等成熟工具。不适合做大规模团队协作设计。没有数据库级的并发管理方案。不适合做高密度高速板设计。没有阻抗控制、差分对、等长布线这些高级规则检查能力。2.3 安全与合规边界原理图编辑器本身不涉及版权风险但如果你打算在项目里导入第三方元件库、封装库必须确认这些库的授权方式。商业 EDA 工具的库文件通常受版权保护直接解析并搬运会有法律风险。更稳妥的做法是自行建立符号库或者使用开源许可的库文件。如果后续扩展了仿真、网表导出、PCB 自动布局等能力导出文件仅限自有测试环境使用不要用来源不明的脚本批量生成设计文件避免夹带恶意宏代码。3. 手搓原理图编辑器的整体设计思路在动手写代码之前先明确原理图编辑器要解决的核心问题。我们用的原理图工具表面上是画图实际上是一个电气连接模型编辑器。你拉一条导线软件要记录的是这根线从哪个元件的哪个引脚出发经过哪些折点最终连接到哪个元件的哪个引脚。你放一个网络标号软件要记录的是这个标号代表一个电气网络所有挂在同标号下的引脚都是等电位的。所以手搓原理图编辑器的第一步不是画界面而是定义数据模型。3.1 核心数据模型一个最小可用的原理图编辑器至少需要六类数据对象对象说明示例元件实例画布上放置的一个元件包含位号、坐标、旋转角度、所属符号库 IDR1、C2、U1符号定义元件的外观和引脚定义不包含具体位号和坐标电阻符号、电容符号、运放符号引脚元件的电气连接点包含名称、编号、电气类型VCC、GND、IO1导线连接两个引脚之间的电气线段从 U1.3 到 R1.1网络标号给一组电气连接点命名NET_12V、GND总线/标签层次化或分组连接用数据线 D0-D7用数据结构表示大概是class SymbolPin: name: str number: str x: float y: float electrical_type: str # input, output, power, ground, passive class Component: reference: str # 位号 symbol: SymbolDef x: float y: float rotation: float properties: dict # 参数如电阻值、封装 class WireSegment: start: tuple end: tuple net_name: str所有画布上的操作最终都会转化为对这些数据对象的增删改查。3.2 画布交互层设计画布交互层是用户能直接看到的部分。它负责把上面的数据模型渲染成图形并接收鼠标事件进行编辑。最小可行方案需要实现视图变换缩放和平移将世界坐标映射到屏幕坐标。选中与框选鼠标点击选中元件或导线拖拽矩形框多选。拖拽移动拖动元件时所有挂在该元件引脚上的导线端点要跟着移动。连线绘制从引脚出发自动生成横平竖直的导线折线。橡皮筋效果拖动导线端点时未落定的部分跟随鼠标实时重绘。这一步最容易出现的问题是“坐标系混乱”。建议从一开始就统一用世界坐标保存模型数据只在渲染层做坐标变换。不要在鼠标事件回调里直接拼接像素坐标否则缩放级别一变化所有数据都会错位。3.3 电气连接识别电气连接识别是原理图编辑器区别于普通画图工具的关键功能。当你放置完元件、画好导线后软件需要回答一个问题哪些引脚是电气连通的常用做法是基于网络连通性扫描构建引脚和导线的邻接关系。把导线端点视为节点。从任意引脚出发做广度优先遍历。所有遍历到的引脚归入同一个网络。网络最终会得到一个名字。如果用户手动放置了网络标号就以该标号命名为准否则自动分配一个编号。这个逻辑直接决定后续网表导出的质量。如果连接识别有误导出的网表就是错的整个后端流程全部白搭。4. 环境准备与前置条件手搓原理图编辑器不需要高配机器但开发环境需要提前准备好。4.1 操作系统与运行时建议使用 Python 3.9 以上版本配合 PySide6 或 Tkinter 做桌面界面。PySide6 的图形交互能力强适合做画布类应用Tkinter 则胜在零额外依赖但复杂交互会写得很痛苦。当前项目设计上不依赖任何商业 EDA 组件所有绘图和交互逻辑都可以基于 Qt 的 QGraphicsView 框架实现。QGraphicsView 本身提供了场景、图元、视图三层结构非常适合做原理图画布。如果你选 Qt 路线核心依赖只有两个pip install PySide6 pip install lxmllxml 用于导出 XML 格式的原理图文件。如果不想引入额外依赖也可以改用 Python 标准库的 ElementTree。4.2 硬件要求CPU双核以上即可没有特殊指令集要求。内存4GB 够用。原理图文件在工程里是最小的数据类型之一几千个元件的工程内存占用也就在几百 MB 级别。GPU完全不需要。原理图编辑器是 2D 渲染Qt 自带的软件渲染即可流畅运行。磁盘源码加依赖 500MB 左右主要是 PySide6 的体积。4.3 目录结构建议schematic_editor/ ├── core/ # 核心数据模型 │ ├── symbol.py │ ├── component.py │ ├── wire.py │ └── net.py ├── ui/ # 界面与交互 │ ├── canvas.py │ ├── toolbar.py │ └── inspector.py ├── io/ # 文件读写与导出 │ ├── schematic_reader.py │ ├── schematic_writer.py │ └── netlist_exporter.py ├── lib/ # 元件符号库 │ ├── basic.json │ └── logic.json ├── examples/ │ └── demo_project/ └── main.py这个目录结构把一个原理图编辑器按职责拆开后面替换图形框架或增加 Web 前端都相对容易。5. 安装部署与启动方式5.1 源码下载与依赖安装假设你已经有项目源码进入根目录后cd schematic_editor python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install -r requirements.txtrequirements.txt 内容参考PySide66.5.0 lxml4.9.05.2 命令行启动python main.py --project examples/demo_project启动后会自动创建一个空白文档并加载 examples 目录下的元件库到左侧面板。如果你的系统是 Windows也可以直接双击start.batecho off cd /d %~dp0 call venv\Scripts\activate.bat python main.py --host 127.0.0.1 --port 8080 pause这里--host和--port是为了后续 Web/API 模式预留的参数。如果不需要网络接口直接忽略即可。5.3 界面布局启动后的界面从左到右大致分为三块左侧元件符号库面板点击元件即可在画布上放置。中间原理图画布支持缩放、平移、框选、连线。右侧属性检查器显示当前选中元件或导线的参数。建议把符号库面板做成可折叠画布区域尽量大。原理图编辑器的使用效率很大程度上取决于可视区域的大小。6. 功能测试与效果验证启动服务后接下来按功能模块逐项测试。6.1 元件放置与移动测试测试目的确认符号库可以正常加载元件能放置到画布上并能移动。操作步骤在左侧元件库中选择一个电阻符号R。点击画布中心区域放置一个电阻实例。拖拽该电阻到新位置。查看右侧属性检查器确认坐标值跟随变化。预期结果元件跟随鼠标移动坐标属性实时更新画布无残影。判断标准元件移动时内部引脚标记跟随移动撤销操作能恢复原位置。常见问题问题现象可能原因排查方式元件列表为空符号库 JSON 解析失败查看终端日志检查 JSON 格式元件放置后不显示场景坐标与视图坐标未同步检查 QGraphicsScene 的 sceneRect 设置拖动时元件闪烁未开启双缓冲渲染设置 QGraphicsView 的 viewportUpdateMode6.2 导线绘制与连接测试测试目的验证导线能否从元件引脚出发并正确吸附到另一个引脚。操作步骤放置两个电阻 R1、R2。点击 R1 的右侧引脚拖动鼠标到 R2 的左侧引脚。释放鼠标后观察导线是否自动生成横平竖直的折线。再次点击导线确认它能被选中和删除。预期结果导线端点精确吸附在引脚中心位置导线颜色比普通线段深一些表示已建立电气连接。判断标准删除导线后两端引脚的电气连接关系解除重新绘制导线后连接恢复。这里的核心是引脚吸附算法。如果从引脚出发的连线总是偏移几个像素检查引脚坐标是否做了视图变换的逆转换。6.3 网络标号与电源符号测试测试目的验证网络标号能否正确归类到电气网络。操作步骤放置一个 VCC 电源符号。再放置一个 GND 电源符号。在两条不同的导线上分别放置网络标号5V_A和5V_B。导出网表观察5V_A和5V_B是否被识别为不同网络。预期结果网表文件中出现两个独立网络。这个功能建议单独测试。网络标号是原理图设计中最容易出问题的环节尤其在层次化设计中。6.4 文件保存与重新加载测试测试目的验证原理图数据能持久化保存。操作步骤在前几步的基础上点击“保存”按钮。关闭编辑器。重新启动打开刚才保存的文件。检查所有元件位置、导线连接、网络标号是否完全恢复。预期结果文件重新打开后元件位置和连接关系与保存前一致。判断标准重新加载的文件导出的网表与保存前导出的网表完全一致。这是最严格的标准。6.5 网表导出测试测试目的把原理图转换为后端工具可用的网表格式。操作步骤在画布上搭建一个简单的 LED 串联电阻电路。点击“导出网表”。用文本编辑器打开生成的.net文件。预期输出示例伪格式实际以项目实现为准* Netlist Generated by SchematicEditor * .NET 5V_A R1.1 C1.1 C1.2 GND .NET GND R1.2 GND判断标准每个元件的引脚都出现在正确的网络中网络名与原理图上的标号一致。7. 接口 API 与数据格式设计原理图编辑器如果只做 GUI价值有限。真正要接入自动化流程必须提供文件格式和可编程接口。7.1 原理图文件格式项目采用 XML 格式保存原理图数据根节点结构如下schematic version0.1 library pathlib/basic.json / components component refR1 symbolRES x120.0 y80.0 rotation0 property nameValue value10k / property nameFootprint value0805 / /component /components wires wire idw1 start_refR1 start_pin1 end_refC1 end_pin1 vertex x120.0 y100.0 / /wire /wires netlabels netlabel net5V_A refC1 pin1 / /netlabels /schematic这种格式的好处是文本可读方便调试。可以用 lxml 做模式校验。与其他工具对接时只需要写一个转换脚本。7.2 Python 调用示例即使不启动 GUI也可以通过命令行脚本完成原理图解析和网表导出实现批量任务。from core.document import SchematicDocument doc SchematicDocument() doc.load(examples/demo_project/main.xml) # 获取所有元件 components doc.get_components() for comp in components: print(comp.reference, comp.symbol_name, comp.x, comp.y) # 获取网络列表 nets doc.build_nets() for net in nets: print(NET:, net.name) for pin in net.pins: print( , pin.component_ref, pin.pin_name) # 导出网表 doc.export_netlist(output/demo.net)7.3 批量任务与脚本接口批量任务在 EDA 工具链里非常实用。比如批量检查所有元件是否有位号和封装。批量导出多个原理图文件对应的网表。批量生成物料清单。代码示例import glob import sys from core.document import SchematicDocument from core.checker import Checker files glob.glob(projects/**/*.xml, recursiveTrue) checker Checker() fail_count 0 for f in files: doc SchematicDocument() doc.load(f) errors checker.run(doc) if errors: fail_count 1 print(f[FAIL] {f}) for e in errors: print( -, e) else: print(f[PASS] {f}) sys.exit(1 if fail_count 0 else 0)如果你打算做 Web 版接入可以把核心模块封装成 HTTP 服务。这里给出一个通用 API 示例接口路径需要按项目实际调整。from flask import Flask, request, jsonify from core.document import SchematicDocument app Flask(__name__) app.route(/api/open, methods[POST]) def open_schematic(): data request.get_json() path data.get(path) doc SchematicDocument() doc.load(path) return jsonify(doc.to_dict()) app.route(/api/netlist, methods[POST]) def generate_netlist(): data request.get_json() path data.get(path) doc SchematicDocument() doc.load(path) nets doc.build_nets() return jsonify([{name: n.name, pins: [p.to_dict() for p in n.pins]} for n in nets]) if __name__ __main__: app.run(host127.0.0.1, port8080)调用方式curl -X POST http://127.0.0.1:8080/api/netlist \ -H Content-Type: application/json \ -d {path: examples/demo_project/main.xml}这里只是演示接口能力。实际项目中建议把文件路径参数改为文件内容上传避免接口直接暴露服务器目录结构。8. 资源占用与性能观察原理图编辑器不像 AI 模型那样吃显存但一样有性能观察点。8.1 内存占用用 Raster 类软件测试一个 500 元件、2000 节点的原理图Python PySide6 进程的内存占用大约在 200MB 到 400MB 之间。这个数据仅供参考实际以本机测试为准。观察方式# Linux / macOS top -p $(pgrep -f python main.py) # Windows 任务管理器 # 按内存排序找到 python.exe8.2 帧率与交互流畅度影响交互流畅度的主要因素元件数量。画布缩放级别。是否开启了网格吸附。导线折点数。如果发现拖动时卡顿优先优化方案是关闭抗锯齿渲染。对不可见图元做裁剪不参与绘制。使用空间索引加速碰撞检测。避免在鼠标移动事件里做完整网络重建。网络重建只在导线落定时触发即可。8.3 CPU 推理与数据集规模虽然原理图编辑器不涉及 CPU 推理但连通性扫描算法的时间复杂度值得关注。如果采用 BFS 遍历网络当元件数量到达几千级时每次修改导线都要重新扫描全部网络性能会明显下降。优化策略增量式网络更新只更新受影响区域的连接关系。缓存网络分配结果导线修改时先判断是否影响已有网络再决定是否全量重建。9. 常见问题与排查方法问题现象可能原因排查方式解决方案启动后窗口黑屏PySide6 渲染后端异常查看终端是否输出 OpenGL 错误尝试设置软件渲染在 main.py 开头设置os.environ[QT_QUICK_BACKEND] software元件库加载失败JSON 路径错误或格式非法用 Python json 模块单独解析库文件修正 JSON 格式或使用绝对路径加载导线无法吸附到引脚引脚坐标未注册到场景的吸附点集合检查引脚创建时是否调用了register_snap_point()在引脚数据更新后重新注册吸附点网络标号未识别标号对象未关联到网络对象检查放置标号时是否调用了bind_label_to_net()绑定逻辑前移在标号创建时立即建立关联网表导出后引脚缺失元件引脚电气类型未配置检查符号库中引脚定义在符号库 JSON 中补充electrical_type字段撤销操作失效命令栈未记录元件移动操作检查移动结束事件是否 push 了 UndoCommand为移动操作实现单独的 UndoCommand 类中文元件名乱码文件编码未统一检查读写文件时的 encoding 参数统一使用encodingutf-8画布缩放到极大值后崩溃坐标精度溢出在缩放事件中限制 scale 的范围设置最小缩放 0.1最大缩放 10.0API 服务端口被占用8080 端口已被其他服务使用使用netstat -ano | findstr 8080查看占用进程更换端口或启动前检测端口占用并自动选择新端口10. 手搓原理图编辑器的关键设计细节10.1 元件符号库设计符号库是这个项目的灵魂。没有符号库画布就是一张白纸。建议符号库使用 JSON 格式每个符号包含{ name: RES, pins: [ {name: 1, x: -25.0, y: 0.0, electrical_type: passive}, {name: 2, x: 25.0, y: 0.0, electrical_type: passive} ], graphics: [ {type: rect, x1: -25.0, y1: -5.0, x2: 25.0, y2: 5.0}, {type: line, x1: -25.0, y1: 0.0, x2: 25.0, y2: 0.0} ], default_properties: { Value: 1k, Footprint: 0603 } }符号库的 pin 坐标必须与 graphics 坐标使用同一坐标系这样才能保证引脚和图形对齐。10.2 层次化设计支持当原理图变大单张图纸放不下时需要支持层次化设计。做法是引入子图对象。每个子图相当于一张独立画布通过端口符号与外层连接。核心数据模型增加class SubSheet: name: str file_path: str ports: list # 端口符号列表在网表导出时递归展开子图把子图内部的网络合并到顶层网络中。这个逻辑不复杂但要注意递归深度的控制避免循环引用导致死循环。10.3 快捷键与交互效率原理图编辑器的操作频率远高于普通绘图软件快捷键直接决定使用效率。推荐实现的一组快捷键快捷键功能W进入连线模式Space放置当前选中的元件R旋转选中元件X镜像翻转选中元件Del删除选中对象CtrlZ / CtrlY撤销/重做G切换网格吸附F2编辑位号10.4 物料清单生成物料清单是原理图编辑器给用户最直接的工程输出之一。实现逻辑遍历画布上所有元件。提取位号、符号名、属性中的 Footprint 和 Value。按 Footprint Value 分组统计数量。输出 CSV 或 Excel。import csv def generate_bom(doc, output_path): groups {} for comp in doc.get_components(): key (comp.symbol_name, comp.properties.get(Value), comp.properties.get(Footprint)) groups.setdefault(key, []).append(comp.reference) with open(output_path, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([Symbol, Value, Footprint, References, Quantity]) for (symbol, value, footprint), refs in groups.items(): writer.writerow([symbol, value, footprint, ,.join(refs), len(refs)])这里用utf-8-sig编码是为了让 Excel 打开 CSV 时中文不乱码。这个细节在实际工程里非常有用。11. EDA 工具链的扩展方向原理图编辑器只是 EDA 工具链的第一个环节。做完这个后续可以扩展的方向很多。11.1 网表到 PCB 的桥接原理图编辑器输出的网表是 PCB 编辑器的输入。PCB 编辑器需要做的是读取网表建立元件封装和网络的对应关系。根据封装库中的焊盘坐标生成初始布局。人工拖拽摆放后调用布线器完成连接。这个项目可以对接开源 PCB 编辑器也可以自研 PcbNew 兼容导出让用户把网表直接导入 KiCad 完成后续设计。11.2 电路仿真接入原理图编辑器建好电气连接后可以导出 Spice 子电路文件。在编辑器上增加仿真按钮调用开源仿真器把波形结果画在画布下方的仿真面板里。这需要为元件符号增加 Spice 模型映射比如{ name: RES, spice: { template: R{ref} {pin1} {pin2} {value} } }11.3 规则检查类似立创 EDA 的 DRC设计规则检查功能在原理图阶段主要包括未连接的引脚。输出类型引脚直接短路。位号重复。元件缺少封装。网络标号悬空。这些检查项实现起来都不复杂就是遍历数据模型做判断。有了规则检查编辑器才能从“画图工具”变成“设计工具”。11.4 Web 化部署如果桌面版做好了可以再套一层 Web 界面。方案有两种前端用 Canvas 绘制原理图后端仍然用 Python 核心处理数据和网表。桌面版编译为本地服务浏览器通过 WebSocket 与本地服务通信。第二种方案更简单也更容易保留原有交互逻辑的稳定性。12. 最佳实践与使用建议12.1 从最小功能闭环开始第一版不要全功能齐上。先把“放置元件 → 画线 → 自动生成网络 → 导出网表”的最小闭环跑通。这个过程会让你对数据模型有足够深的理解后面加任何功能都是在闭环上做加法。12.2 统一坐标系统这是整个项目中最容易出现隐患的地方。建议在代码里统一所有坐标使用浮点世界坐标仅在渲染层转换为屏幕坐标。不要在业务逻辑里混用像素坐标。12.3 保存文件定期做版本标识原理图文件格式调整是常态。建议在文件根节点中写入生成该文件的软件版本号这样即使在后续版本中格式发生变化旧文件也能通过迁移逻辑正确读取。schematic version0.3 generatorSchematicEditor-0.1.012.4 接口服务限制访问范围如果开放了 HTTP API 服务尽量绑定在 127.0.0.1避免局域网其他设备直接访问。如果需要跨机器使用建议在前面加一层鉴权。12.5 保持符号库和代码的版本同步符号库是数据文件也应有版本号。当引脚定义或图形结构发生变化时版本号能帮助你快速判断旧原理图文件的兼容性。13. 总结与后续路线这一期把手搓 EDA 软件的原理图编辑器完整拆了一遍。核心收获可以归纳为原理图编辑器的本质是电气连接模型编辑器不是画图工具。数据模型是顶层设计画布交互是它的可视化投影。网络连通性分析、网表导出、物料清单是最容易出成果的三个功能模块。符号库设计决定了整个工具的上限投入时间在这里最值得。当前版本最适合用来做学习、演示、二次开发和内部自动化工具链基础。最先应该验证的功能是放置一个电阻拉一根线到另一个电阻导出网表看看网络表里的连接关系对不对。这个闭环跑通整个架构就稳了。最容易踩的坑是坐标系统不统一和网络连接关系在文件重载后丢失。建议把文件保存/加载测试放在开发流程很靠前的位置而不是等所有功能都做完再补。后续可以沿两条路继续扩展一条是往深走做规则检查、仿真接入、PCB 网表桥接把工具链做完整另一条是往宽走做 Web 版界面、多人协作、元件库云端同步。两条路都有大量工程细节也都有非常清楚的落地路径。如果你是电子类工程师想拿它做课程设计建议在现有核心上先做物料清单导出和 DRC 检查这两个功能容易出效果也方便演示。如果你是想做产品化工具建议先补足撤销重做、快捷键、剪贴板这些基础体验再考虑网络功能。这个项目的价值在于它把常用的商业 EDA 工具从“黑盒”变成了“白盒”。当你在嘉立创 EDA 或 KiCad 里操作的时候就能按这一套数据模型去理解后面发生了什么。理解到这个层级工具就不再只是工具而是可以改造成自己的生产力系统。建议收藏备用尤其在寒假和课程设计季动手搓一个原理图编辑器比单纯调库画板更能体现工程能力。