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

CATIA 二次开发:3分钟批量重命名装配体零件号

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

一、痛点引入

接外协模型的工程师都见过这种装配体:几十个零件,零件号清一色的 Part1、Part2……或者公司编码规则升级,几百个旧号码要全部换成新码。手动改的流程是双击属性、改号、确定、保存,一个零件30秒,100个零件一小时起步——这还是手不打滑的前提下。更糟的是改到一半撞了重号,装配体里出现两个"BRK-012",BOM一导出就是一场事故。

这不是你手速慢,是工具没跟上。今天教你用 Python 把"改零件号"压缩成两步:导出清单、填表回写。


二、核心思路

先厘清一个概念,这是本篇的关键:装配树上有三层名字——实例名(树节点显示名)、零件号(PartNumber,BOM和工程图里用的)、文档(CATPart/CATProduct文件本身)。零件号是文档级属性:改一次,装配体里所有引用同步生效。所以批量改号的正确姿势是按"文档"去重,而不是按实例逐个改——一个垫片被引用50次,也只需要改1次。

CATIA装配体(CATProduct)
  │  python rename_pn.py export
  ▼
pn_list.csv(当前零件号清单)
  │  工程师在Excel里填"新零件号"列
  ▼
python rename_pn.py apply
  │  pywin32  COM接口  CATIA
  ▼
按文档改PartNumber  逐文档Save  完成

你需要准备:

  • CATIA V5(已打开目标装配体)
  • Python 3.x
  • pywin32

三、环境搭建(2分钟)

pip install pywin32

验证连接(CATIA需已启动):

import win32com.client
catia = win32com.client.Dispatch("CATIA.Application")
print(catia.ActiveDocument.Name)   # 输出当前文档名即连接成功

四、核心代码

# -*- coding: utf-8 -*-
"""
rename_pn.py —— 装配体零件号批量重命名(两步工作流)
第一步:python rename_pn.py export  -> 导出零件号清单 pn_list.csv
第二步:在Excel里填"新零件号"列,再运行 python rename_pn.py apply
"""
import sys
import csv
import os
import win32com.client

CATIA = win32com.client.Dispatch("CATIA.Application")

# ========== 第1步:递归展开装配树 ==========
def walk(product, depth=0, out=None):
    """返回 [(实例Product, 层级)],根节点在最前"""
    if out is None:
        out = []
    out.append((product, depth))
    kids = product.Products                # 属性:不带括号
    for i in range(1, kids.Count + 1):     # Count 是属性
        walk(kids.Item(i), depth + 1, out) # Item 是方法:带括号
    return out

def collect_docs(root):
    """按"文档"去重:同一零件被引用N次只保留一份。
    返回 {文档完整路径: 该文档的参考Product}"""
    docs = {}
    for p, _ in walk(root)[1:]:            # [1:] 剔除根装配体自身
        ref = p.ReferenceProduct           # 实例 -> 参考对象
        docs.setdefault(ref.Parent.FullName, ref)  # 文档级去重
    return docs

# ========== 第2步:导出零件号清单 ==========
def do_export(root, csv_path):
    docs = collect_docs(root)
    with open(csv_path, "w", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        w.writerow(["当前零件号", "文档路径", "新零件号"])  # 新零件号留空待填
        for ref in docs.values():
            w.writerow([ref.PartNumber, ref.Parent.FullName, ""])
    print(f"已导出 {len(docs)} 个文档 -> {csv_path}")

# ========== 第3步:按清单回写新零件号 ==========
def do_apply(root, csv_path):
    docs = collect_docs(root)
    mapping = {}                           # {文档路径: 新零件号}
    with open(csv_path, encoding="utf-8-sig") as f:
        for row in csv.DictReader(f):
            new = row["新零件号"].strip()
            if new:
                mapping[row["文档路径"].strip()] = new
    if len(set(mapping.values())) != len(mapping):
        sys.exit("存在重复的新零件号,请先检查CSV")
    # 新零件号不得与"不参与改名"的现有零件号重复
    keep = {ref.PartNumber for path, ref in docs.items() if path not in mapping}
    clash = set(mapping.values()) & keep
    if clash:
        sys.exit(f"新零件号与现有零件号冲突:{clash}")

    done = fail = 0
    for path, ref in docs.items():
        if path not in mapping:
            continue
        old = ref.PartNumber
        try:
            ref.PartNumber = mapping[path]     # 零件号是文档级属性
            ref.Parent.Save()                  # 改完立即保存该文档
            print(f"[OK] {old} -> {mapping[path]}")
            done += 1
        except Exception as e:
            print(f"[失败] {old}: {e}")
            fail += 1
    print(f"完成:成功 {done} 个,失败 {fail} 个")

if __name__ == "__main__":
    doc = CATIA.ActiveDocument             # 先在CATIA中打开装配体
    if not doc.Name.lower().endswith(".catproduct"):
        sys.exit("请先激活一个 CATProduct 装配体")
    root = doc.Product
    csv_path = os.path.join(doc.Path, "pn_list.csv")
    if "export" in sys.argv:
        do_export(root, csv_path)
    elif "apply" in sys.argv:
        do_apply(root, csv_path)
    else:
        print("用法:python rename_pn.py export|apply")

代码解析:

  • walk():递归展开装配树。注意 pywin32 动态绑定下 Count 是属性(不带括号)、Item(i) 是方法(带括号),写反了直接报错;
  • ReferenceProduct + Parent:把实例换算成参考对象,再取它所在的文档——零件号真正存储在文档上,这正是"按文档去重"的落点;
  • PartNumber = 新号:文档级属性,同一零件被引用50次也只需改1次;
  • utf-8-sig:带BOM的CSV,Excel双击打开中文不乱码。

五、效果对比

对比项传统方法自动化脚本
耗时(100个零件)60分钟以上导出1秒+填表+回写约5分钟
重号风险高(肉眼核对)极低(脚本自动查重)
漏改/错改常见无(清单驱动)
保存逐个Ctrl+S逐文档自动Save
可重复性每次从头来规则变更改一行重跑

六、进阶用法

技巧一:规则化重命名,连表都不用填——把所有默认名 Part1/Part2 一次刷成"前缀-流水号":

def rule_rename(root, prefix):
    """默认名 Part1/Part2... 一次刷成 前缀-001 格式"""
    seq = 1
    for ref in collect_docs(root).values():
        if ref.PartNumber.startswith("Part"):
            ref.PartNumber = f"{prefix}-{seq:03d}"
            ref.Parent.Save()
            seq += 1
    print(f"已按规则重命名 {seq - 1} 个零件")

rule_rename(root, "BRK-2026")   # 结果:BRK-2026-001、BRK-2026-002 ...

技巧二:实例名同步零件号,改完号把树节点显示名一并刷新,模型树清爽一截:

for p, _ in walk(root)[1:]:
    if p.Name != p.ReferenceProduct.PartNumber:
        p.Name = p.ReferenceProduct.PartNumber

两个提醒:脚本改的是零件号,文件名不会变,需要连文件名一起改得用"另存";A改B、B改C这种链式改名请分两批执行。另外建议先复制一份装配体试跑,确认无误再上真数据。


关注回复「零件号」获取完整源码

下一篇:CAA C++ 踩坑第6篇——Undo/Redo 事务回滚,让插件不再"一失手成千古恨"。


_栏目:开发实战 | 发布日期:2026-09-19 | 字数:约1050字_

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

咨询深入资源 →