# Python + ODA + ezdxf: CAD自动化处理解决方案 ## 🎯 项目目标 本部分旨在构建一个基于 Python 的自动化工作流程,用于对 CAD 文件(特别是 `.dwg` 和 `.dxf`)进行深度处理。它结合了外部转换工具的格式兼容性和 `ezdxf` 库强大的编程处理能力,以实现标准化的、可重复的、自动化的图纸操作。 ## ✅ 当前能力 截至目前,本解决方案已具备以下核心能力: 1. **DWG to DXF 自动转换**: 通过调用 ODA File Converter,将 `.dwg` 文件无损转换为 `.dxf` 格式,作为后续处理的起点。 2. **读取与查询 (Read/Query)**: * 加载并解析 DXF 文件的完整结构。 * 使用强大的查询语句,精确查找符合特定条件的图形实体(例如,`查询所有颜色为蓝色的直线` 或 `查询特定图层上的所有文字`)。 * 访问图形实体的所有属性(如坐标、长度、颜色、图层、文字内容等)。 3. **写入与修改 (Write/Modify)**: * 修改现有图形实体的属性(例如,批量更改颜色、移动位置)。 * 在图纸中添加新的图形实体,例如: * **添加文字**: 在指定坐标或相对某个元素的位置精确插入单行或多行文字。 * **添加几何图形**: 创建新的线条、圆、多段线等。 4. **格式转换 (Export)**: * 将处理后的 DXF 内容高质量地渲染并输出为 **PDF** 文件,适用于存档和打印。 * **模板化绘图**: 基于对现有图纸的几何分析,精确提取其完整的样式规范(包括所有线段、文字、尺寸、颜色、图层、对齐方式等),并将其固化为一个可复用的JSON模板。结合外部数据源,可以在任何DXF文件中**100%精确地复制**出符合原始设计意图的表格。 ## ⚠️ 前置要求 在运行脚本之前,你 **必须** 满足以下两个条件: 1. **安装 ODA File Converter**: * 这是一个免费的工具,用于在不同版本的 DWG 和 DXF 之间进行转换。 * 请从 [Open Design Alliance 官网](https://www.opendesign.com/guestfiles/oda-file-converter) 下载并安装。 * 安装后,请**务必**检查 `run_workflow.py` 等脚本中的 `ODA_CONVERTER_PATH` 变量,确保其指向你电脑上 `OdaFileConverter.exe` 的正确路径。 2. **设置 Python 虚拟环境**: * 本项目已配置为使用虚拟环境,以避免与其他项目的依赖冲突。 * 运行 `venv/Scripts/pip install -r requirements.txt` 来安装所有必要的依赖。 ## 📂 文件说明 - `venv/`: Python 虚拟环境目录。 - `requirements.txt`: 项目依赖库列表。 - `run_workflow.py`: 核心工作流脚本,演示 DWG -> DXF 转换和批量颜色修改。 - `add_text_to_specific_line.py`: **高级示例**。演示如何查询特定条件(颜色、长度)的线段,并在其上方精确添加文字。 - `convert_dxf_to_pdf.py`: **高级示例**。演示如何将 DXF 文件内容渲染为 PDF。 - `图签测试.dxf`: 用于测试的源 DXF 文件。 - `extract_entities_to_json.py`: **(步骤1)** 从一个作为“模板”的DXF文件中,提取所有线、文字及其完整属性(坐标、图层、颜色、对齐方式),并保存为一个JSON文件(“数字快照”)。 - `generate_template_from_json.py`: **(步骤2)** 读取“数字快照”,分析其结构和样式,并生成**两个**独立的、模块化的模板文件:`header_template.json` (定义表头几何) 和 `columns_template.json` (定义数据列样式)。 - `draw_table_from_template.py`: **(步骤3)** 最终的“绘图引擎”。它会加载模块化的模板,并结合外部数据源 (一个JSON文件),在目标DXF中精确绘制表格。 - `header_template.json` / `columns_template.json`: 由步骤2生成的核心模板文件,实现了表头样式和数据列样式的分离。 - `test_workflow.py`: **(测试)** 一个自动化的测试脚本,用于执行完整的“绘图 -> 提取 -> 验证”流程,确保整个工作流的正确性和稳定性。 - `test_bom_data.json`: 为测试脚本提供标准化的输入数据。 ## 🚀 如何运行 (模板化绘图工作流) ## 如何用于您自己的图纸 (分步指南) 本指南将向您展示如何使用您自己的DXF文件(包含一个您想复制的表格样式),来为您自己的数据创建模板,并最终将其绘制到任何目标DXF文件中。 ### 准备工作:制作一个符合规范的“模板源DXF文件” 为了让脚本能正确工作,您需要准备一个DXF文件,它**必须**包含一个结构清晰的表格,并且这个表格应满足以下条件: 1. **包含完整的表头**:位于表格的最底部。 2. **至少包含一行示例数据**:位于表头的正上方。 您可以参考项目中的 `04_Test_Files/Drawing1.dxf` 作为标准示例。 ### 第1步:从您的DXF中创建表格模板 假设您有一个名为 `My_Table_Style.dxf` 的文件,其中包含一个几何、样式都非常标准的表格。我们的目标就是将这个表格的“外观”提取出来,做成可复用的模板。 **1.1. 将DXF的几何信息提取为JSON快照** 运行 `extract_entities_to_json.py` 脚本,告诉它读取您的DXF文件,并将结果保存为一个“数字快照”JSON文件。 ```shell # 用法: python extract_entities_to_json.py <您的源DXF> <输出的JSON快照> ./venv/Scripts/python 03_Python_OpenSource_DXF/extract_entities_to_json.py "C:/path/to/My_Table_Style.dxf" 03_Python_OpenSource_DXF/my_snapshot.json ``` **1.2. 从JSON快照生成模块化模板** 现在,运行 `generate_template_from_json.py` 脚本,它会读取上一步生成的快照文件,并自动分析、拆分,生成两个核心模板。 ```shell # 用法: python generate_template_from_json.py <输入的JSON快照> <输出的表头模板> <输出的列模板> ./venv/Scripts/python 03_Python_OpenSource_DXF/generate_template_from_json.py 03_Python_OpenSource_DXF/my_snapshot.json 03_Python_OpenSource_DXF/my_header_template.json 03_Python_OpenSource_DXF/my_columns_template.json ``` 完成这一步后,您就已经成功地将您图纸中的表格样式“数字化”并保存为了 `my_header_template.json` 和 `my_columns_template.json`。 ### 第2步:准备您的数据 接下来,您需要按照指定的格式创建一个JSON文件来存放您想填入表格的数据。您可以参考项目中的 `test_bom_data.json` 文件。 核心格式是一个列表,其中每个对象代表一行。对象的键(Key)必须与您的 `columns_template.json` 中定义的 `name` 相匹配。 **`my_data.json` 示例:** ```json [ { "件 号": {"main": "A-001"}, "名 称": {"chinese_name": "轴承", "english_name": "Bearing"}, "数量": {"main": "10"}, "材 料": {"main": "GCr15"}, "单": {"main": "5.5"}, "总": {"main": "55.0"}, "备 注": {"main": "进口"} }, { "件 号": {"main": "A-002"}, "名 称": {"chinese_name": "螺栓", "english_name": "Bolt"}, "数量": {"main": "100"}, "材 料": {"main": "35#"}, "单": {"main": "0.2"}, "总": {"main": "20.0"}, "备 注": {"main": "M8x20"} } ] ``` 请将您的数据保存为一个文件,例如 `my_data.json`。 ### 第3步:将数据绘制到目标DXF文件 万事俱备!现在运行 `draw_table_from_template.py` 脚本,将**您的模板**、您的数据和您的目标文件作为参数传入。 最重要的,您现在可以通过 `--x` 和 `--y` 参数来精确指定表格**左下角的插入坐标**。 ```shell # 用法: python draw_table_from_template.py <目标DXF> <您的表头模板> <您的列模板> <您的数据JSON> <输出DXF> --x --y ./venv/Scripts/python 03_Python_OpenSource_DXF/draw_table_from_template.py "C:/path/to/target_drawing.dxf" "C:/path/to/my_header_template.json" "C:/path/to/my_columns_template.json" "C:/path/to/my_data.json" "C:/path/to/output_with_table.dxf" --x 300 --y 100 ``` 执行完毕后,`output_with_table.dxf` 文件中就会在 (300, 100) 的位置出现一个根据您的样式和数据绘制好的、崭新的表格。 --- ### (附) 运行自动化测试 为了确保所有脚本功能正常,可以随时运行测试套件。 ```shell # 运行端到端测试 ./venv/Scripts/python 03_Python_OpenSource_DXF/test_workflow.py ``` ## 📘 核心概念:模板化绘图 我们的解决方案核心是将CAD设计规范转化为一个机器可读的JSON模板。当前版本的核心工作原理基于一个简单而明确的几何假设: - **基于几何的区域识别**: 脚本通过分析DXF文件中的所有**水平线**来识别表格的行结构。 - **“表头在底部”的核心假设**: 脚本**假定表格最底部的区域是表头**,而表头之上的区域是数据区。为了让模板能被正确提取,您作为模板的源DXF文件应遵循此结构。 - **精确复制**: 脚本会提取出表头区域内所有的文字、线条及其完整的几何属性(坐标、尺寸、对齐方式、颜色、图层等),确保生成的表头与原始设计100%一致。 - **数据模板化**: 脚本会分析紧邻表头上方的第一行数据,并将其样式(文字的相对位置、字体、高度等)作为后续所有数据行的统一模板。 ## 📘 核心操作指南 (ezdxf) 本节提供核心的代码片段,作为未来功能开发的快速参考。 ### 1. 读取和查询 DXF 文件 ```python import ezdxf # 加载 DXF 文件 try: doc = ezdxf.readfile("图签测试.dxf") msp = doc.modelspace() # 获取模型空间 # --- 查询操作 --- # 1. 查询所有颜色为蓝色(5)的直线 query = 'LINE[color==5]' blue_lines = msp.query(query) print(f"找到了 {len(blue_lines)} 条蓝色的线。") # 2. 遍历查询结果并访问属性 for line in blue_lines: start_point = line.dxf.start end_point = line.dxf.end length = start_point.distance(end_point) print(f" - 线段长度: {length:.2f}, " f"起点: ({start_point.x:.2f}, {start_point.y:.2f})") except IOError: print("无法读取文件。") except Exception as e: print(f"发生错误: {e}") ``` ### 2. 修改和写入 DXF 文件 ```python import ezdxf from ezdxf.math import Vec3 # 加载 DXF 文件 try: doc = ezdxf.readfile("图签测试.dxf") msp = doc.modelspace() # --- 修改操作 --- # 1. 将所有图元颜色改为红色(1) for entity in msp: if entity.dxf.hasattr('color'): entity.dxf.color = 1 # 1 代表红色 # --- 写入/添加操作 --- # 2. 添加一个新的文字样式 (如果不存在) if "MyTextStyle" not in doc.styles: # 使用宋体 (simsun.ttc)。确保操作系统中存在此字体 doc.styles.new("MyTextStyle", dxfattribs={"font": "simsun.ttc"}) # 3. 在指定位置添加多行文字 (MTEXT) msp.add_mtext( "这是新添加的文字", dxfattribs={ 'char_height': 10.0, # 文字高度 'style': 'MyTextStyle', # 应用我们创建的样式 'color': 2, # 颜色为黄色 } ).set_placement( insert=Vec3(100, 100, 0), # 插入点坐标 align=ezdxf.enums.TextEntityAlignment.MIDDLE_CENTER ) # 4. 使用对齐方式添加单行文字 (TEXT) msp.add_text( "中心对齐", height=2.5, dxfattribs={ 'style': 'Standard', 'color': 3, } ).set_placement( Vec3(200, 100, 0), align=ezdxf.enums.TextEntityAlignment.MIDDLE_CENTER ) # 保存修改后的文件 output_path = "修改后的文件.dxf" doc.saveas(output_path) print(f"文件已成功保存到: {output_path}") except IOError: print("无法读取文件。") except Exception as e: print(f"发生错误: {e}") ```