
1. 这个项目要解决的到底是个什么活1.1 先说明白哪些场景真正需要“批量生成CAD图纸”接触过CAD的人都知道真正折磨人的往往不是画一张图而是反复画同一类图。工程出图、项目交付、施工变更很多时候图纸的核心结构完全一样只是尺寸、编号、标注、图层名称不同。这时候如果还靠人工一张张改不仅效率低还特别容易出错——改完这一张忘了那一张编号漏改、尺寸对不上回头检查比重新画一遍还累。“python批量生成CAD图纸”这句话本质上是在解决“重复劳动自动化”的问题。我见过最典型的几个场景批量生成桩位图、预埋件图、节点大样图结构相同但坐标或参数不同给一批既有图纸批量修改标注、图层、文字内容比如统一把某层改成红色、把所有标题栏编号按规则重排按数据库里的设备清单批量出图每一台设备对应一张安装示意图图纸格式批量转换批量合并多张图纸或者批量把CAD文件转成PDF用于归档。这些问题的共同点是流程确定、规则明确、但数量巨大。只要规则能写清楚Python就能替你一张张画完而且不会疲劳、不会漏项。1.2 两条技术路线驱动CAD本体 vs 直接读写DXF我刚接触这个方向的时候被两个词搞混过pyautocad和ezdxf。后来用多了才明白它们完全是两种思路。pyautocad是“驱动AutoCAD本体”。它通过COM接口让Python像操作鼠标键盘一样去控制AutoCAD软件可以创建图元、修改对象、调用AutoCAD自带的命令甚至执行打印和导出。优点是功能完整AutoCAD能做的一切它基本都能做缺点是你电脑上必须装了AutoCAD脚本运行时还要保持CAD打开速度自然会慢一些。ezdxf是“纯Python读写DXF文件”。DXF是AutoCAD的开放交换格式本质上是带格式的文本文件。ezdxf可以直接在Python里创建直线、圆、文字、标注、图层、块然后另存为DXF文件整个过程完全不依赖AutoCAD软件。优点是快、轻、适合批量生成还能跑在Linux服务器上缺点是生成的是DXF文件如果下游必须用DWG还得再经过一次转换。我的经验是批量生成新图纸优先用ezdxf批量修改已有图纸或者必须保留DWG格式时用pyautocad。后面我会把两条路线都演示一遍方便你对号入座。2. 环境准备与最关键的选型决策2.1 装Python和依赖库实际只需要三步不管走哪条路线Python环境得先就位。如果你之前没装过Python直接去官网下载安装包安装时记得勾选“Add Python to PATH”否则命令行里找不到python命令。这里不展开讲网上一搜一大把注意别下到奇怪的“高速下载器”就行。装好Python之后在命令行里执行pip install ezdxf pyautocad如果只走ezdxf路线装ezdxf就够如果要操作AutoCAD就把pyautocad一起装上。装完可以跑一下验证python -c import ezdxf; print(ezdxf.__version__) python -c import pyautocad; print(pyautocad.__version__)能输出版本号就说明环境OK。这里有个小坑pyautocad依赖pywin32如果你的Python是64位AutoCAD也是64位一般没问题万一导入报错单独再装一次pywin32通常能解决。2.2 为什么我建议先搞懂DXF而不是一上来就开CAD我见过不少新人一听说“Python批量生成CAD图纸”第一反应就是去装各种插件、找破解补丁折腾半天软件没弄好热情先灭了。其实换个思路如果只是生成新图纸完全没必要启动AutoCAD这么重的软件。DXF文件的本质是什么你可以在记事本里打开任何一个DXF文件看到大量成对出现的数字和字符串那其实是“组码”和“值”。比如0后面跟LINE表示“下面要定义一个直线实体”10、20、30分别表示起点的X、Y、Z坐标11、21、31表示终点的坐标。这是一种极简的、程序友好的数据协议。ezdxf把这种协议封装成了Python对象你不用关心底层组码细节直接用msp.add_line((0,0), (100,50))就能画一根线。对于批量生成图纸来说这个库就是你的“画笔工厂”。更关键的是DXF文件可以直接用ezdxf读写、修改、合并整个过程不需要安装任何CAD软件。这意味着你可以在没有图形界面的服务器上跑批量任务每天深夜自动生成几百张图纸第二天早上交付这才是“批量”的正确姿势。2.3 pyautocad和ezdxf怎么配合才不算重复造轮子实际工作里两条路线经常混用。我自己的习惯是能用ezdxf干的绝不开CAD必须开CAD的才交给pyautocad。比如有一批老图纸需要批量改图层的颜色但手头只有DWG格式没有原始DXF这时候用ezdxf读不了DWG就只能用pyautocad打开DWG、遍历对象、改属性、另存。再比如需求是“根据Excel里的设备参数批量生成100张基础平面图”这种从零开始画图的场景ezdxf绝对是首选。生成的DXF文件如果客户非要DWG可以用CAD打开DXF另存为DWG或者在服务器上用ODAFileConverter这类工具批量转格式。还有一个更省事的组合先用ezdxf批量生成DXF草稿再用pyautocad开CAD批量执行“批量为图纸加图框、加标题栏、输出PDF”等收尾动作。各管一段谁擅长什么就干什么。3. 核心细节解析与实操要点3.1 搞清楚图层、线型、文字样式才是规范出图的前提程序画图很容易画出一堆“能看但不专业”的图。为什么因为很多人只关注线段和圆忽略了图纸的灵魂——图层、线型、文字样式。正规的设计图轮廓线、中心线、标注线、填充都是分层的。批量生成图纸时如果不先把图层规范好最后交付的图纸就是一团乱麻甲方或者下游施工单位根本没法用。用ezdxf创建带图层的图纸核心代码长这样import ezdxf doc ezdxf.new(R2010) doc.layers.add(轮廓线, color7, linetypeCONTINUOUS) doc.layers.add(中心线, color1, linetypeCENTER) doc.layers.add(标注层, color3, linetypeCONTINUOUS) msp doc.modelspace() msp.add_line((0, 0), (100, 0), dxfattribs{layer: 轮廓线}) msp.add_line((50, -10), (50, 10), dxfattribs{layer: 中心线})注意color是AutoCAD的索引色号7是白色/黑色视背景而定1是红色3是绿色。linetype如果不设置默认就是BYLAYER跟着图层走。这里有个细节linetypeCENTER的前提是该线型已经加载到图纸里ezdxf提供doc.setup_modelspace之类的辅助方法但更稳妥的做法是用doc.linetypes.add逐个添加。批量出图时我习惯把图层、线型、文字样式的创建逻辑封装成一个独立的初始化函数每张新图都调用一次保证所有图的基础配置一致。3.2 文字和标注怎么放才不会乱跑CAD图纸里的文字不是随便写个坐标就能对齐的。中文字体、文字高度、对齐方式、旋转角度这些参数直接决定最终出图效果。用ezdxf加文字最常用的方式是add_textmsp.add_text( 节点A-01, dxfattribs{ height: 2.5, layer: 标注层, style: HZ } ).set_placement((10, 10), alignezdxf.const.MIDDLE_CENTER)这里的style指文字样式需要先在文档里定义好尤其是中文字体样式。CAD默认的standard样式对中文支持不太好容易出现问号乱码。我一般在初始化时加一个HZ样式doc.styles.add(HZ, fontsimhei.ttf)set_placement里的align参数控制对齐方式MIDDLE_CENTER是最常用的表示文字中心点对准坐标。如果不设置对齐文字默认从左下角开始排多行文字特别容易错位。标注比文字复杂一些但ezdxf也封装好了线性标注msp.add_linear_dim( base(50, -5), p1(0, 0), p2(100, 0), dxfattribs{layer: 标注层} ).render()这段代码会在两个点之间生成一个水平标注。记得调用render()否则标注不会真正生成到图纸里这个坑我踩过两次才记住。3.3 批量循环时参数千万别写死在代码里所谓“批量”核心是把变化的部分抽出来变成循环里的变量。我见过最糟糕的写法是把100个坐标直接粘在代码里效果跟手工画没区别。正确的思路是数据放外部Excel、CSV、JSON、数据库代码里只留“读取数据→生成图元”的逻辑。比如我要批量生成一批定位板图纸每张图的孔径、位置不同数据放在holes.csv里编号,圆心X,圆心Y,半径,板宽,板高 A001,30,40,8,120,80 A002,55,20,10,150,100Python这边就循环读取import csv import ezdxf with open(holes.csv, encodingutf-8) as f: for row in csv.DictReader(f): doc ezdxf.new(R2010) # 初始化图层、样式... msp.add_rectangle((0, 0), (float(row[板宽]), float(row[板高]))) msp.add_circle((float(row[圆心X]), float(row[圆心Y])), float(row[半径])) doc.saveas(foutput/板件_{row[编号]}.dxf)提取参数的意义不仅在于方便批量还在于“改一处全部生效”。如果明天孔径变了只改CSV不用动一行代码。这才是批量生成的真正价值。4. 实操过程用ezdxf从零生成一张完整图纸4.1 以“100张预埋件定位图”为例搭出可复制的框架我拿一个实际跑过的需求给你完整过一遍流程项目方要给一百多个预埋件分别出一张定位图每张图就是一个矩形板加四个圆孔、一条中心线、一个编号文字参数全在Excel里。第一步先建模板思路。我跟同事核对清楚图纸比例、图层名称、图框大小、文字高度这些必须全项目统一于是先固定了一个配置字典CONFIG { layer_outline: 轮廓线, layer_center: 中心线, layer_dim: 标注层, font: simhei.ttf, text_height: 3.0, id_prefix: YMJ-, }第二步写一个创建单张图的函数。这个函数只干一件事输入参数输出一张完整的DXFdef create_plate_dxf(plate_id, width, height, holes, output_path): doc ezdxf.new(R2010) msp doc.modelspace() # 初始化图层、文字样式 doc.layers.add(轮廓线, color7) doc.layers.add(中心线, color1, linetypeCENTER) doc.layers.add(标注层, color3) doc.styles.add(HZ, fontCONFIG[font]) # 画外轮廓 msp.add_lwpolyline( [(0, 0), (width, 0), (width, height), (0, height), (0, 0)], dxfattribs{layer: 轮廓线} ) # 画中心线 msp.add_line((0, height / 2), (width, height / 2), dxfattribs{layer: 中心线}) # 画孔 for cx, cy, r in holes: msp.add_circle((cx, cy), r, dxfattribs{layer: 轮廓线}) # 写编号 msp.add_text( CONFIG[id_prefix] plate_id, dxfattribs{height: CONFIG[text_height], style: HZ, layer: 标注层} ).set_placement((width / 2, height 5), alignezdxf.const.MIDDLE_CENTER) doc.saveas(output_path)注意这里用了add_lwpolyline而不是四条add_line原因是多段线在CAD里是一个整体对象后期选中、修改、导入其他软件都更方便文件体积也更小。第三步写批量入口。import csv with open(precast_holes.csv, encodingutf-8) as f: for row in csv.DictReader(f): holes [] for i in range(1, 5): cx float(row[f孔{i}X]) cy float(row[f孔{i}Y]) r float(row[f孔{i}R]) holes.append((cx, cy, r)) create_plate_dxf( plate_idrow[编号], widthfloat(row[板宽]), heightfloat(row[板高]), holesholes, output_pathfoutput/{row[编号]}.dxf )跑完以后输出目录里就有了一百多张DXF。用CAD打开抽查几张图层、线型、编号都规范直接就能交付。整个过程从写脚本到出图不到半小时。4.2 加标题栏和图框用“块”来复用如果每张图还要加图框和标题栏千万别每张图都重新画一遍。ezdxf支持“块Block”相当于一个可重复使用的零件库。先定义一个图框图块再在每张图的模型空间里插入这个块def add_title_block(msp, title_text, block_nameTITLE_BLOCK): block msp.block # 实际应该用doc.blocks # 注意块应该在doc级别定义而不是msp严谨一点的写法是这样def setup_title_block(doc, block_nameTITLE_BLOCK): block doc.blocks.new(nameblock_name) # 在块里画图框线 block.add_line((0, 0), (420, 0)) block.add_line((420, 0), (420, 297)) block.add_line((420, 297), (0, 297)) block.add_line((0, 297), (0, 0)) # 在块里写标题栏文字 block.add_text(项目名称, dxfattribs{height: 5}).set_placement((10, 10)) block.add_text(图号, dxfattribs{height: 5}).set_placement((10, 20)) return block def add_title_block_to_drawing(msp, title_text): msp.add_blockref(TITLE_BLOCK, (0, 0), dxfattribs{layer: 图框层})插入块用add_blockref传入块名和插入点坐标。一个块可以反复被插入到任何图纸里天然适配批量场景。而且块内部的所有图元是作为一个整体存在的后续想统一改图框样式只需要改块定义所有引用它的图纸在CAD里重新打开时都会更新。4.3 批量合并图纸其实很简单说到“图纸合并”这也是一个常见的衍生需求。用ezdxf合并多张DXF的核心思路是读取每张图的模型空间图元逐个复制到目标文档里。import ezdxf def merge_dxf_files(file_list, output_file): target_doc ezdxf.new(R2010) target_msp target_doc.modelspace() for idx, path in enumerate(file_list): source_doc ezdxf.readfile(path) source_msp source_doc.modelspace() # 把源图的所有图元复制到目标图 target_msp.add_entities(source_msp) # 如果需要把每张图平移到不同位置 # 这里可以加变换操作 target_doc.saveas(output_file)这里有两个细节。一个是target_msp.add_entities(source_msp)ezdxf会尽量保留原图元属性但复制过来后图层、文字样式如果和目标文档不一致CAD打开时可能显示异常。所以我会在合并前把所有源图的图层、样式也一并合并到目标文档里。另一个是如果多张图合并后需要分行错位排版可以给每张图源图元加一个平移向量这需要遍历图元并修改坐标可以用ezdxf的transform相关方法实现类似msp.translate或者遍历每个实体做矩阵变换但通常直接定位每张图的原点更省事。合并后的图纸如果觉得文件太乱还可以对这个合并文档执行一次“清理”doc.cleanup()cleanup()会清理未使用的图层、样式、线型等定义让文件保持干净。合并前的源文件如果有重复图层名但属性不同合并后也会以目标文档为准这也是为什么要统一模板的原因。5. 批量修改已有CAD图纸pyautocad实战5.1 用ActiveX控制AutoCAD时的几个关键动作如果你的输入是DWG文件或者需要修改别人发来的图纸ezdxf就显露出局限性了它读不了DWG。这时候pyautocad登场。pyautocad的基本用法很简单几步就能跑通from pyautocad import Autocad acad Autocad(create_if_not_foundTrue) print(acad.doc.Name)这段代码会连接当前打开的AutoCAD如果没有CAD在运行会自动创建一个新文档。连接成功后就可以读取、修改文档内容。比如遍历模型空间所有文本对象把文字“旧版”批量改成“新版”from pyautocad import Autocad acad Autocad(create_if_not_foundTrue) for entity in acad.iter_objects(): if entity.ObjectName AcDbText: if entity.TextString 旧版: entity.TextString 新版这段代码很短但承载着大批量改文字的最常见需求。同理想批量改图层颜色for entity in acad.iter_objects(): if entity.Layer 某层名: entity.color 1 # 改成红色iter_objects()默认遍历模型空间。如果图里还有布局空间的对象得自己额外处理。还有一点要注意pyautocad对中文支持有时候不稳如果文字是中文在脚本里直接写字符串一般没问题但如果字符串来源是文件要注意编码统一用utf-8避免乱码。5.2 批量改完图后自动另存和导出PDF批量修改的最后一个动作通常是把图另存为新DWG或者导出PDF。用pyautocad执行保存有两种常见路径acad.doc.SaveAs(rD:\output\改后图纸.dwg)如果要做PDF导出最简单的办法是调用AutoCAD自带的打印命令acad.doc.Plot.QuietErrorMode True acad.doc.Plot.PlotToFile(rD:\output\改后图纸.pdf)注意PDF导出依赖AutoCAD里的页面设置最好在模板里提前配好打印机为“DWG To PDF.pc3”、纸张大小、打印区域。否则导出的PDF可能出现空白或者范围不对。这个属于CAD操作层面的事但很多人卡在这我提一句免得你白踩坑。批量处理DWG时我还会给每个文件开“只读”保护避免脚本运行中手滑改了源文件acad.Documents.Open(path, ReadOnlyTrue)这种细节不跑上几次批量脚本你是意识不到的。6. 常见问题与排查技巧实录6.1 先给一张问题速查表问题原因解决方案DXF里中文显示成问号文字样式没用中文字体在文字样式中设置simhei.ttf或simsun.ttf标注调用了render()但图纸里看不到标注所在图层被关闭或冻结检查图层的on状态和frozen状态ezdxf读取已有DWG报错ezdxf只支持DXF/DXF格式用pyautocad或先把DWG另存为DXF合并图纸后部分图元“消失”目标文档缺少源文档的图层/线型定义合并前逐一添加源文档的图层、线型、样式pyautocad导入报错pywin32没装或版本不对单独执行pip install pywin32CAD批量导出PDF空白页面设置不符合要求在CAD中预设“DWG To PDF.pc3”打印机和打印范围批量生成图纸文件体积太大每张图拷贝大量重复块定义用块引用BlockRef复用而不是复制图元这张表里的问题我几乎全都遇到过。“标注看不见”那个问题最坑因为代码没有报错只怪当时图层设置成了关闭状态。这提醒我批量出图前一定要“检查一遍图层状态”。6.2 排查思路先出小批量全流程再放大规模批量任务最忌讳一上来就循环一万次。我的习惯是第一步先跑一个只有3-5条数据的样例人工打开DXF检查每个图元的位置、图层、文字、标注。这一步能拦掉90%的规则错误比如坐标算错了、文字重叠了、图层命名不对。第二步做一次全量生成但抽查输出文件目录结构、文件数量、文件大小是否合理。比如100个文件总大小应该在一定范围内突然出现某个文件大小异常多半就是内容缺失或者运行时报错被吞了。第三步批量导入CAD或者用工具批量转一次PDF抽查几张最终效果的清晰度、图框完整性。这套三步走的方法不是谁教的是我跑了七八次批量任务之后总结出来的。最惨的一次一晚上生成了300多张图第二天给客户一打开发现线型全是实线因为源文件里的CENTER线型在合并时没有正确带入。后来每次跑完我都先打开三五张图看看中心线是不是虚线再决定要不要批量交付。6.3 几个值得养成的“保命习惯”第一用相对路径和中间产物管理你的文件。批量生成过程中肯定会产生大量中间文件。我的项目目录一般是这样的project/ ├── data/ # 原始参数文件Excel/CSV/JSON ├── scripts/ # Python脚本 ├── output_dxf/ # 生成的DXF文件 ├── output_dwg/ # 转格式后的DWG文件 └── logs/ # 运行日志脚本跑完看一眼output_dxf目录里文件数量对不对比肉眼一张张看快多了。第二每张图纸都必须写入“生成时间”和“版本号”。可以用ezdxf给文档写扩展数据或者放在图纸的标题栏里。一次跑了三十多版参数最后客户说“第一版其实就行”你要是没记录版本号想死的心都有。doc.header[$CUSTOM] generated by python script v1.2这只是示意实际用标题栏文字能看得更直观。我把版本号、生成时间、参数来源文件名都写在图框的一角交付的时候特别省事。第三脚本日志一定要有。不输出到命令行也要输出到文件。import logging logging.basicConfig(filenameflogs/run_{time_str}.log, levellogging.INFO)批量任务跑到一半如果挂了日志能立刻告诉你挂在第几条数据上。没有日志你就只能从头跑浪费时间还容易漏错。7. 再看一个进阶场景用Python自动给CAD图纸做批量编号7.1 编号规则是什么怎么在脚本里落地图纸编号是工程交付最烦人但又必须做的事。规则往往是“项目号-专业-序号”这种组合比如SJ2024-GY-001。手动编复制粘贴极易出错用Python编规则清晰几秒钟搞定。我实际做过的一个需求一张图里有几十个零件框每个框都要编一个不重复的号号要写在指定位置还要跟Excel里的清单一一对应。这个需求用ezdxf做特别顺手import ezdxf import csv part_list [] with open(parts.csv, encodingutf-8) as f: for row in csv.DictReader(f): part_list.append(row) # 假定包含 序号, 位置X, 位置Y, 规格 doc ezdxf.readfile(base.dxf) msp doc.modelspace() for i, part in enumerate(part_list, start1): number f{part[规格]}-{i:03d} x float(part[位置X]) y float(part[位置Y]) msp.add_text(number, dxfattribs{height: 3.0, style: HZ} ).set_placement((x, y), alignezdxf.const.MIDDLE_CENTER) doc.saveas(numbered.dxf)这个脚本虽然短但背后有个关键点零件位置必须先从CAD图纸里提取出来。如果图纸结构规范可以直接从图元里读如果图纸是别人画的可能需要人工先标记一次锚点。所以“批量”的前提往往是“一次规范整理”别指望完全零人工。7.2 如果目标是批量改DWG结合pyautocad更好用上面那套是直接写DXF。如果你的源图纸是DWG而且需要保留原文件格式那就用pyautocad来做同样的编号逻辑from pyautocad import Autocad, APoint acad Autocad(create_if_not_foundTrue) doc acad.doc msp doc.ModelSpace for i in range(1, 30): text msp.AddText(f编号-{i:03d}, APoint(50, 10 * i), 2.5)AddText是AutoCAD的ActiveX接口前两个参数是内容和插入点第三个是文字高度。APoint用来构造坐标点。这里又出现一个中文注意项如果AddText里的字符串是中文请确认CAD里当前文字样式支持中文否则显示又是问号。7.3 用参数模板代替写死的脚本才是可持续的批量化真正做多了你会发现每一个“一次性”的批量任务背后其实都能沉淀成一套模板。代码不要写死在脚本里把“哪些图层、什么线型、文字多高、编号前缀、文件命名规则”都抽成配置项最好是一个config.yaml或config.py。配置化之后下次遇到类似需求只需要改配置文件脚本几乎不动。这是我从十几次批量化项目里得到的最大感悟。很多人觉得“这次需求很急先硬写一气”但硬写的脚本往往第二周就废了。花20分钟抽一个配置层后面能省两小时还不止。8. 写在最后的一点个人体会这套“Python批量生成CAD图纸”的方法我前前后后跑了两年多。从最初给朋友帮忙改图到后来在项目里正式承担批量出图任务最深的感受就一句话批量生成不是让你抛弃CAD而是让你把重复操作从人手里拿出去。CAD仍然是你最强大的设计和审图工具Python只是替你做那些不需要判断、只需要执行的事情。如果你第一次上手我建议你从最简单的需求开始比如拿三五个圆孔板试试ezdxf生成DXF再打开CAD确认效果。你可能会发现自己第一次写的脚本虽然能跑但图层、线型、文字对齐都差着意思。这很正常把这些规范补上你的脚本才算真正能交付。最后分享两个小技巧一个是用ezdxf生成图纸时文件名里最好带上生成时间和版本号避免后面找不到上一版另一个是批量交付前一定抽出三张图用CAD“人肉”看一眼别完全信任脚本输出眼睛扫一遍能发现很多代码里看不出来的问题。批量出图这条路一旦走通你会发现它比你想象的还要香。祝你第一张批量生成的图纸顺利落地。