dxfedit/03_Python_OpenSource_DXF/README.md
puzzlesion 5e87bd7c33 refactor(dxf): optimize table drawing and add diagnostic scripts
This commit includes several updates to the Python_OpenSource_DXF project:

- The core script draw_table_from_template.py has been refactored for better logic and clarity.

- New utility scripts such as diagnose_blocks.py, export_dxf.py, and plot_by_block_name.py have been added to enhance diagnostic and plotting capabilities.

- The convert_dxf_to_pdf.py script was removed as its functionality is now covered by other modules.

- README.md and .specstory documentation have been updated to reflect these changes.
2025-09-12 17:21:08 +08:00

12 KiB
Raw Blame History

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 官网 下载并安装。
    • 安装后,请务必检查 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文件。

# 用法: 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 脚本,它会读取上一步生成的快照文件,并自动分析、拆分,生成两个核心模板。

# 用法: 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.jsonmy_columns_template.json

第2步准备您的数据

接下来您需要按照指定的格式创建一个JSON文件来存放您想填入表格的数据。您可以参考项目中的 test_bom_data.json 文件。

核心格式是一个列表其中每个对象代表一行。对象的键Key必须与您的 columns_template.json 中定义的 name 相匹配。

my_data.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 参数来指定插入坐标,并通过 --anchor 参数来定义这些坐标所对应的锚点

锚点选项:

  • bottom-left (默认): (x, y) 是表格的左下角。
  • bottom-right: (x, y) 是表格的右下角。
  • top-left: (x, y) 是表格的左上角。
  • top-right: (x, y) 是表格的右上角。
# 用法: python draw_table_from_template.py <目标DXF> <您的表头模板> <您的列模板> <您的数据JSON> <输出DXF> --x <X坐标> --y <Y坐标> --anchor <锚点>
./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 420 --y 297 --anchor top-right

执行完毕后,output_with_table.dxf 文件的右上角将被精确定位在 (420, 297) 坐标点,并出现一个根据您的样式和数据绘制好的、崭新的表格。


(附) 运行自动化测试

为了确保所有脚本功能正常,可以随时运行测试套件。

# 运行端到端测试
./venv/Scripts/python 03_Python_OpenSource_DXF/test_workflow.py

📘 核心概念:模板化绘图

我们的解决方案核心是将CAD设计规范转化为一个机器可读的JSON模板。当前版本的核心工作原理基于一个简单而明确的几何假设

  • 基于几何的区域识别: 脚本通过分析DXF文件中的所有水平线来识别表格的行结构。
  • “表头在底部”的核心假设: 脚本假定表格最底部的区域是表头而表头之上的区域是数据区。为了让模板能被正确提取您作为模板的源DXF文件应遵循此结构。
  • 精确复制: 脚本会提取出表头区域内所有的文字、线条及其完整的几何属性坐标、尺寸、对齐方式、颜色、图层等确保生成的表头与原始设计100%一致。
  • 数据模板化: 脚本会分析紧邻表头上方的第一行数据,并将其样式(文字的相对位置、字体、高度等)作为后续所有数据行的统一模板。

📘 核心操作指南 (ezdxf)

本节提供核心的代码片段,作为未来功能开发的快速参考。

1. 读取和查询 DXF 文件

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 文件

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}")