我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Python ezdxf 实战:DXF 批量处理与自动化出图指南

Python ezdxf 实战:DXF 批量处理与自动化出图指南 简介ezdxf 是一个与 DXF 版本无关的 Python 程序包面向需要处理 CAD 图纸的开发者用于创建、读取、修改和保存 DXF 文件。它支持 R12 至 R2018 多个版本能保留未知标签方便打开含第三方数据的工程图且跨平台兼容 CPython 与 pypy3。资源包共 1250 个文件约 20.12MB以 717 个 py 源码为核心辅以 178 个 rst 文档、131 个 png 示意图、63 个 dxf 示例图纸及 37 个 ttf 字体等另含少量 pyx、pxd 等 Cython 加速文件结构完整。目前已有 2418 人学习下载。通过该包读者可获取完整源码、测试用例与文档理解 DXF 底层标签解析与保留机制掌握跨版本读写、实体创建及数学类 Cython 优化思路适合作为 CAD 自动化开发与二次集成的参考工具。1. 用 ezdxf 把 DXF 从黑匣子变成可编程数据如果你在机械、建筑、电气或测绘行业做过二次开发大概率遇到过这种场景甲方甩来一个几百 MB 的 DXF让你批量改图层颜色、提取所有标注文字、按规则生成下料图或者把几千个图元按坐标重新排布。用 CAD 软件手动点点到手抽筋想写脚本又发现 DXF 格式本身像个黑匣子——组码、段、表、块、实体嵌套光看规范就劝退。ezdxf 就是为这个场景生的。它是纯 Python 实现的 DXF 读写库不依赖 AutoCAD不依赖 ODA装完就能在服务器、容器、CI 流水线里跑。你可以把它理解成「用 Python 操作 DXF 的接口层」读进来是 Python 对象改完再写回去还是合法 DXF。适合谁适合需要批量处理图纸的工程师、做 CAD 插件后端的人、以及想把图纸数据接进 Web 或数据库的开发者。这一章先把「它到底能干什么、边界在哪」讲清楚后面几章直接上可复现的代码和参数。2. 读 DXF 之前先搞懂ezdxf 的文档模型与版本选择2.1 DXF 的段、表、块、实体在 ezdxf 里怎么映射DXF 文件本质是一堆「组码 值」的键值对按段SECTION组织。常见段有 HEADER、TABLES、BLOCKS、ENTITIES、OBJECTS。ezdxf 把这套结构抽象成Drawing对象你拿到它之后大部分操作不用再碰组码。核心映射关系如下DXF 概念ezdxf 对象说明整个文件Drawing入口对象ezdxf.readfile()返回图层表doc.layers可增删改图层含颜色、线型线型表doc.linetypes虚线、点划线等定义块定义doc.blocks块内可含多个实体实体段doc.modelspace()模型空间最常用图纸空间doc.layouts布局和视口头变量doc.header$INSUNITS、$EXTMIN等读文件的最小代码import ezdxf # readfile 会自动识别 DXF 版本返回 Drawing 对象 doc ezdxf.readfile(input.dxf) # 模型空间是绝大多数图元所在处 msp doc.modelspace() # 遍历所有实体dxftype() 返回如 LINE、CIRCLE、TEXT for entity in msp: print(entity.dxftype(), entity.dxf.layer)逻辑说明readfile内部会做一次结构校验遇到损坏文件会抛DXFStructureError。modelspace()返回的是Layout对象支持迭代和索引。entity.dxf是一个命名空间访问的是该实体的 DXF 属性比如layer、color、linetype。参数上readfile还有encoding和errors两个可选参数处理非 UTF-8 的老图纸时用得上。2.2 版本选择R12 还是 R2018别等翻车才后悔ezdxf 支持从 R12 到 R2018 的读写但不同版本能力差异很大。选错版本轻则丢数据重则写出的文件打不开。R12最兼容几乎所有 CAD 都能读。但不支持LWPOLYLINE之外的很多新实体不支持OBJECTS段图层颜色索引有限。R2000支持LWPOLYLINE、HATCH、DIMENSION等是很多老系统的默认。R2007 及以上支持 UTF-8 编码、ACAD扩展、更丰富的对象。R2013/R2018支持最新实体和属性但部分老 CAD 读不了。我一般这样选如果下游是未知的第三方系统输出用 R2000如果只在内部现代 CAD 之间流转用 R2010 或 R2018。新建文档时# 新建 R2010 文档默认单位设为毫米 doc ezdxf.new(dxfversionR2010, setupTrue) doc.header[$INSUNITS] 4 # 4 表示毫米setupTrue会预置一些标准线型和文字样式省得自己建。$INSUNITS的值1 英寸、2 英尺、4 毫米、5 厘米、6 米。这个头变量影响插入块时的缩放不设的话不同单位图纸拼在一起会出玄学问题。2.3 用 ezdxf 读一份图纸并统计图元类型下面这段代码可以直接抄用来快速摸清一份陌生图纸的构成import ezdxf from collections import Counter doc ezdxf.readfile(input.dxf) msp doc.modelspace() # 统计模型空间里每种实体出现次数 counter Counter(e.dxftype() for e in msp) # 统计图层使用情况 layer_counter Counter(e.dxf.layer for e in msp) print(实体类型分布, counter.most_common()) print(图层使用分布, layer_counter.most_common()) # 查看图纸范围用于后续坐标处理 extmin doc.header.get($EXTMIN) extmax doc.header.get($EXTMAX) print(图纸范围, extmin, extmax)逻辑说明Counter直接吃生成器表达式内存友好。doc.header.get在头变量不存在时返回None不会抛异常。$EXTMIN/$EXTMAX是图纸范围做批量平移或缩放前先看这个能避免把图元移到视口外。参数上如果图纸很大遍历msp会慢可以改用msp.query(LINE CIRCLE)只取关心的类型query支持类似 SQL 的字符串语法。3. 写 DXF 与批量改图从建图层到生成下料图3.1 建图层、设颜色、加线型的正确顺序写 DXF 时图层和线型必须先于实体创建否则实体引用的图层不存在写出的文件在 CAD 里会显示异常。正确顺序是先建线型再建图层最后加实体。import ezdxf doc ezdxf.new(R2010, setupTrue) msp doc.modelspace() # 1. 确保线型存在setupTrue 已带 CONTINUOUS 和 DASHED if DASHED not in doc.linetypes: doc.linetypes.add(DASHED, pattern[0.5, 0.25, -0.25]) # 2. 建图层指定颜色索引和线型 doc.layers.add(CUT, color1, linetypeCONTINUOUS) # 红色 doc.layers.add(HIDDEN, color3, linetypeDASHED) # 绿色 # 3. 加实体显式指定图层 msp.add_line((0, 0), (100, 0), dxfattribs{layer: CUT}) msp.add_circle((50, 50), radius20, dxfattribs{layer: HIDDEN}) doc.saveas(output.dxf)逻辑说明doc.layers.add的color是 AutoCAD 颜色索引ACI1 红、2 黄、3 绿、4 青、5 蓝、6 洋红、7 白/黑。linetype必须是已存在的线型名否则会抛DXFValueError。dxfattribs是通用属性字典除了layer还能传color、linetype、lineweight。lineweight单位是 1/100 毫米比如 25 表示 0.25 mm。3.2 批量修改已有图纸的图层与颜色实际工作里更常见的是改图不是从零画。下面这个脚本把指定图层上的所有实体改成另一个图层并统一颜色import ezdxf doc ezdxf.readfile(input.dxf) msp doc.modelspace() OLD_LAYER 0 NEW_LAYER PROCESSED # 确保目标图层存在 if NEW_LAYER not in doc.layers: doc.layers.add(NEW_LAYER, color5) count 0 for entity in msp: if entity.dxf.layer OLD_LAYER: entity.dxf.layer NEW_LAYER entity.dxf.color 256 # 256 表示随层 count 1 print(f迁移了 {count} 个实体) doc.saveas(processed.dxf)逻辑说明entity.dxf.layer可直接赋值ezdxf 会在保存时写回组码。color256是 BYLAYER即颜色跟随图层这是最推荐的做法避免硬编码颜色导致后期改图层颜色时实体不跟着变。参数上如果只想改模型空间用msp如果要连块内实体一起改需要遍历doc.blocks里的每个块定义。3.3 用块和属性做参数化下料图块BLOCK是 DXF 里复用图元的机制配合属性ATTRIB可以做参数化。下面演示建一个带属性的块并插入import ezdxf doc ezdxf.new(R2010, setupTrue) msp doc.modelspace() # 定义块基点为 (0,0) block doc.blocks.new(namePART) block.add_lwpolyline([(0, 0), (50, 0), (50, 30), (0, 30)], closeTrue) block.add_attdef(PART_NO, (5, 5), dxfattribs{height: 3}) # 插入块参照并填属性 insert msp.add_blockref(PART, (100, 100)) insert.add_auto_attribs({PART_NO: A-001}) doc.saveas(block_demo.dxf)逻辑说明blocks.new创建块定义add_attdef定义属性模板add_blockref在模型空间插入参照。add_auto_attribs会根据块内的ATTDEF自动生成ATTRIB并填值。参数上add_attdef的height是文字高度insert的第二个参数是插入点。如果块需要缩放或旋转add_blockref还接受dxfattribs{xscale: 2, rotation: 90}。4. 避坑与排查ezdxf 实战里最容易翻车的 5 个点4.1 中文乱码编码不是 UTF-8 就全乱现象读进来的 TEXT 内容全是问号或乱码。原因老 DXF 用 GBK 或 ANSI 编码readfile默认按 UTF-8 解。解决显式传编码。doc ezdxf.readfile(old.dxf, encodinggbk)如果不知道编码可以先用chardet猜或者用errorsreplace先读进来看看。写文件时R2007 及以上默认 UTF-8R2000 及以下需要确认目标系统能读。4.2 实体被“吞”块内实体不在 modelspace 里现象明明图上有图元遍历msp却找不到。原因这些图元在块定义里通过INSERT引用。解决遍历doc.blocks或者用msp.query(INSERT)找到块参照后递归展开。for insert in msp.query(INSERT): block doc.blocks.get(insert.dxf.name) for e in block: print(e.dxftype())注意块可以嵌套递归时要防循环引用。4.3 保存后文件变大没清理无用对象现象改了几个实体保存后文件从 10 MB 涨到 50 MB。原因ezdxf 默认保留所有对象包括已删除实体的残留。解决保存前调用doc.audit()清理。doc.audit() doc.saveas(clean.dxf)audit会修复无效引用并移除孤立对象。如果还大可以用ezdxf.recover模块做更激进的恢复。4.4 坐标对不上单位与 UCS 没统一现象插入的块位置偏移巨大或者尺寸差 25.4 倍。原因图纸单位是英寸代码按毫米算。解决读$INSUNITS统一换算。units doc.header.get($INSUNITS, 0) if units 1: # 英寸 scale 25.4 elif units 4: # 毫米 scale 1.0UCS 也要注意ezdxf 默认在 WCS 下操作如果图纸有自定义 UCS需要先转换。4.5 性能瓶颈逐实体遍历太慢现象处理 10 万图元的图纸要几分钟。原因Python 层逐实体访问属性开销大。解决用query缩小范围或者用ezdxf的groupby批量处理。# 只取 LINE比全遍历快很多 lines msp.query(LINE[layerCUT])query的过滤语法支持属性比较底层会走优化路径。如果还慢考虑把数据导出成 NumPy 数组处理再写回。5. 进阶用 ezdxf 做图纸校验与自动化出图5.1 写一个图层规范校验器批量收图时最头疼的是图层命名混乱。下面这个校验器检查图层是否在允许列表内并输出报告import ezdxf ALLOWED {0, CUT, HIDDEN, TEXT, DIM} def validate(path): doc ezdxf.readfile(path) msp doc.modelspace() issues [] for e in msp: layer e.dxf.layer if layer not in ALLOWED: issues.append((e.dxftype(), layer)) return issues report validate(input.dxf) for dxftype, layer in report[:20]: print(f违规图层{layer}实体{dxftype}) print(f共 {len(report)} 处)逻辑说明ALLOWED是白名单实际项目里可以从配置文件读。issues收集违规项只打印前 20 条避免刷屏。这个模式可以扩展成检查线型、颜色、文字高度等。5.2 批量出图从 Excel 参数到 DXF假设有一张 Excel每行是一个零件的尺寸要生成对应的 DXF。核心是把 Excel 读成字典列表循环调用绘图函数import ezdxf import csv def make_part(width, height, thickness, out_path): doc ezdxf.new(R2010, setupTrue) msp doc.modelspace() doc.layers.add(OUTLINE, color7) msp.add_lwpolyline( [(0, 0), (width, 0), (width, height), (0, height)], closeTrue, dxfattribs{layer: OUTLINE} ) msp.add_text( fT{thickness}, dxfattribs{height: 5, layer: OUTLINE} ).set_placement((width / 2, height / 2)) doc.saveas(out_path) with open(parts.csv, newline, encodingutf-8) as f: for row in csv.DictReader(f): make_part( float(row[width]), float(row[height]), float(row[thickness]), fpart_{row[id]}.dxf )逻辑说明set_placement设置文字对齐点比直接改insert更可靠。csv.DictReader把表头映射成键避免列顺序变化导致错位。参数上add_text的height是字高set_placement的第二个参数可指定对齐方式默认左对齐。5.3 一个我常犯的错忘了doc.audit()就交付早期做批量出图生成几千个 DXF 直接打包发走结果下游反馈部分文件打不开。排查发现是某些块引用在复制过程中产生了孤立对象。后来养成习惯任何写操作之后、saveas之前先doc.audit()。它多花不到一秒但能省掉来回扯皮。另一个习惯是交付前用ezdxf.recover.readfile再读一遍自己生成的文件能读通才算过关。这两个动作看起来多余实际是性价比最高的后悔药。希望帮到你。本文还有配套的精品资源点击获取
返回列表