MEAICAD预约演示
技术博客/二次开发
二次开发

CATIA 二次开发:一个脚本批量转 STEP/IGES/STL,200 个模型交付不再点一整天

2026-09-07#CATIA#二次开发

一、痛点引入

发数节点到了。工艺要 STEP,供应商要 IGES,3D 打印那边要 STL,质量部要 CGR 做轻量化评审——同一批 200 个模型,四种格式。

你的操作是:文件 另存为 下拉选格式 找目录 保存 关窗口 下一个。一个模型四次,200 个模型就是 800 次点击,中间还要应付"文件已存在,是否覆盖"的弹窗。运气不好点错一次,输出目录里混进两个同名文件,交付前还得返工核对。

更麻烦的是:转换完谁成功了、谁失败了、哪个文件导出是 0 字节,全靠人工翻文件夹。这不是你手速慢,是 CATIA 的"另存为"本来就是给人单条操作设计的,不是给批量交付设计的。

今天教你用 Python COM API,一次扫描目录、自动转换、输出带耗时和体积的交付清单——200 个模型 8 分钟跑完,人可以去干别的。


二、核心思路

CATIA 的 Document 对象带一个 ExportData(iFileName, iType) 方法,本质就是"另存为"的编程版本:告诉它输出路径和格式代号,一次调用完成转换。整个链路只需要三步——打开文档、调 ExportData、关闭文档。

真正的工程难点不在转换本身,而在批量:递归扫目录、屏蔽弹窗、异常隔离(一个坏文件不能中断整批)、结果留痕。

Python脚本  COM接口  CATIA.Application
                        ├─ DisplayFileAlerts = False   屏蔽"是否覆盖"弹窗
                        ├─ RefreshDisplay   = False    批量期间不刷屏,提速30%+
                        ├─ os.walk() 递归扫描 .CATPart / .CATProduct
                        ├─ for f in files:
                        │   ├─ Documents.Open(f)       必须用绝对路径
                        │   ├─ doc.ExportData(out, "stp")
                        │   ├─ os.path.getsize() 校验非0字节
                        │   ├─ except: 记录失败原因,continue(不中断整批)
                        │   └─ finally: doc.Close()
                        └─ csv.writer 输出交付清单(耗时/体积/状态)

你需要准备:

  • Python 3.7+、pywin32
  • 本机已装 CATIA V5(脚本会复用已启动的会话,没有则自动拉起)
  • 一批待转换的 .CATPart / .CATProduct

三、环境搭建(2分钟)

pip install pywin32

验证 COM 通道和导出方法是否可用:

import win32com.client

catia = win32com.client.Dispatch("CATIA.Application")
catia.Visible = True
doc = catia.ActiveDocument
print(f"当前文档: {doc.Name}")
print(f"ExportData 方法可用: {hasattr(doc, 'ExportData')}")
print("转换接口就绪。")

能打出文档名和 True,说明 ExportData 在当前版本可用。老版本(V5R20 之前)如果只有 ExportAs,把调用替换成 doc.ExportAs(out_file, "stp") 即可,参数一致。


四、核心代码

# -*- coding: utf-8 -*-
"""CATIA 批量格式转换:.CATPart / .CATProduct -> STEP / IGES / STL"""
import os, time, csv
import win32com.client as win32

SRC_DIR = r"D:\Project\Models"     # 源模型目录(递归扫描)
OUT_DIR = r"D:\Project\Deliver"    # 输出目录
FORMAT  = "stp"                    # stp / igs / stl / cgr / 3dxml

EXT_MAP = {"stp": ".stp", "igs": ".igs", "stl": ".stl",
           "cgr": ".cgr", "3dxml": ".3dxml"}
SRC_EXT = (".catpart", ".catproduct")


def connect_catia():
    """连接(或拉起)CATIA,并关掉批量场景下的干扰项"""
    catia = win32.Dispatch("CATIA.Application")
    catia.Visible = True
    catia.DisplayFileAlerts = False   # 屏蔽"文件已存在/是否保存"弹窗,关键
    catia.RefreshDisplay = False      # 批量期间不刷新图形区,明显提速
    return catia


def scan_models(src_dir):
    """递归扫描目录下所有 CATIA 模型,返回绝对路径列表"""
    found = []
    for root, _, names in os.walk(src_dir):
        for n in names:
            if n.lower().endswith(SRC_EXT):
                found.append(os.path.join(root, n))
    return sorted(found)


def convert_one(catia, src_file, out_dir, fmt):
    """转换单个文件,返回 (是否成功, 输出路径, 体积KB, 耗时s, 错误信息)"""
    base = os.path.splitext(os.path.basename(src_file))[0]
    out_file = os.path.join(out_dir, base + EXT_MAP[fmt])
    os.makedirs(out_dir, exist_ok=True)

    t0 = time.time()
    doc = None
    try:
        doc = catia.Documents.Open(src_file)   # 必须绝对路径,相对路径会报找不到文件
        doc.ExportData(out_file, fmt)          # 核心:一行完成格式转换
        cost = round(time.time() - t0, 2)
        size = os.path.getsize(out_file) / 1024 if os.path.exists(out_file) else 0
        if size == 0:                          # 0字节=静默失败,必须拦下来
            return False, out_file, 0, cost, "导出为0字节"
        return True, out_file, round(size, 1), cost, ""
    except Exception as e:
        return False, out_file, 0, round(time.time() - t0, 2), str(e)[:120]
    finally:
        if doc is not None:
            doc.Close()                        # 不关会累积占内存,几百个文件后CATIA卡死


def main():
    catia = connect_catia()
    files = scan_models(SRC_DIR)
    print(f"扫描到 {len(files)} 个模型,开始转换...")

    report = [("源文件", "输出文件", "体积KB", "耗时s", "状态", "错误信息")]
    ok = fail = 0
    for i, f in enumerate(files, 1):
        success, out, size, cost, err = convert_one(catia, f, OUT_DIR, FORMAT)
        if success:
            ok += 1
            print(f"[{i}/{len(files)}] OK   {os.path.basename(f)}  {size}KB  {cost}s")
        else:
            fail += 1
            print(f"[{i}/{len(files)}] FAIL {os.path.basename(f)}  {err}")
        report.append((f, out, size, cost, "成功" if success else "失败", err))

    with open(os.path.join(OUT_DIR, "转换清单.csv"), "w",
              newline="", encoding="utf-8-sig") as fp:
        csv.writer(fp).writerows(report)

    catia.RefreshDisplay = True    # 恢复刷新,否则后续手动操作界面不更新
    print(f"\n完成:成功 {ok} 个,失败 {fail} 个,清单已输出到 {OUT_DIR}")


if __name__ == "__main__":
    main()

代码解析:

  • ExportData(out_file, fmt):转换的唯一核心方法。fmt 用格式代号字符串,stp/igs/stl/cgr/3dxml 都支持,不需要装任何转换器。
  • DisplayFileAlerts = False:批量场景的命门。不关的话,重名文件弹一次窗,脚本就卡在那里等你点,所谓的"自动化"直接变成"半自动"。
  • finally: doc.Close():每个文件转完必须关。CATIA 打开几百个文档不释放,内存会一路涨到崩溃。
  • size == 0 判定:CATIA 转换失败时有时不抛异常,只生成一个空文件。只靠 try/except 会漏判,必须加体积校验。

五、效果对比

对比项传统方法(手动另存为)自动化脚本
200个模型耗时约 4 小时约 8 分钟
人工操作次数800+ 次点击1 次运行
弹窗中断每次重名都要点全程无干扰
失败可追溯靠人翻文件夹CSV 清单带错误原因
换格式重做全部重来一遍改一个变量重跑
出错率高(漏转/重名/选错目录)极低(路径由代码生成)

六、进阶用法

技巧1:保留源目录层级,让交付包结构对齐

上面的版本把所有输出平铺在一个目录里,源目录有 机身段/、系统件/ 子目录时结构会丢。加三行就能镜像原结构:

def convert_one(catia, src_file, out_dir, src_root, fmt):
    rel = os.path.relpath(os.path.dirname(src_file), src_root)  # 相对路径
    out_sub = os.path.join(out_dir, rel)                        # 镜像到输出目录
    os.makedirs(out_sub, exist_ok=True)
    out_file = os.path.join(
        out_sub, os.path.splitext(os.path.basename(src_file))[0] + EXT_MAP[fmt])
    ...

交付包直接 zip 发给外协,对方解压后的目录结构和你的一致,不用再口头解释"这个文件原来在哪层"。

技巧2:长批处理的稳定性兜底

跑 500 个以上文件时,个别老模型会触发 CATIA 内部异常导致会话变"僵尸"。两个实用兜底:

# 1) 每 100 个文件重启一次 CATIA 会话,释放累积内存
if i % 100 == 0:
    catia.Quit()
    time.sleep(3)
    catia = connect_catia()

# 2) 装配体导出前强制更新,避免导出到陈旧几何
doc = catia.Documents.Open(src_file)
if hasattr(doc, "Product"):          # 是装配体
    doc.Product.Update()
else:
    doc.Part.Update()
doc.ExportData(out_file, fmt)

另外提醒一句:STL 的弦高精度由 CATIA 选项(工具 选项 常规 性能和精度)控制,脚本本身不改它。交付前先手动导出一个确认精度满足要求,再跑批量,否则 200 个文件全是粗糙网格,返工成本更高。


关注回复「批量转换」获取完整源码 + 转换清单 CSV 模板


_栏目:开发实战 | 发布日期:2026-09-07 | 字数:约 1150 字 | 系列:CATIA Python COM API 入门 #11_

本文为基础实战分享。如需完整源码包、配套课程或企业内训,欢迎进一步了解付费体系。

咨询深入资源 →