一、痛点引入
接外协模型的工程师都见过这种装配体:几十个零件,零件号清一色的 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字_