c4d对象名称批量更名字段替换脚本

2025-09-02 11:08:56 / Directory:Plugin / Views:12

使用说明:

1. 安装使用:

  • 将脚本保存为 .py 文件

  • 在 Cinema 4D 中通过 脚本 → 用户脚本 → 加载脚本 来运行

2. 功能介绍:

序列号重命名:

  • 可以设置前缀(如"对象_")

  • 可以设置起始序号

  • 会自动生成 对象_001对象_002 这样的名称

自定义重命名:

  • 输入新的名称

  • 如果选择多个对象,会自动添加序号

字符替换:

  • 输入要替换的文字(如广告内容)

  • 输入替换成的文字

  • 会在所有对象名称中进行查找替换

3. 操作选项:

  • 重命名所有对象:处理场景中的所有对象

  • 重命名选中对象:只处理当前选中的对象(包括子对象)

4. 安全特性:

  • 支持撤销操作(Ctrl+Z)

  • 操作前会提示确认

  • 显示处理结果统计

# -*- coding: utf-8 -*-
"""
Cinema 4D 批量重命名工具
支持序列号重命名、自定义重命名和字符替换
"""
import c4d
from c4d import gui
# 全局变量定义
DIALOG_ID = 1000
GROUP_MAIN = 2000
GROUP_BUTTONS = 2001
GROUP_RADIO = 2002  # 单选按钮组
# 控件ID
RADIO_SEQUENCE = 3001      # 序列号重命名
RADIO_CUSTOM = 3002        # 自定义重命名
RADIO_REPLACE = 3003       # 字符替换
EDIT_PREFIX = 4001         # 前缀输入框
EDIT_START_NUM = 4002      # 起始序号
EDIT_CUSTOM_NAME = 4003    # 自定义名称
EDIT_OLD_TEXT = 4004       # 要替换的文字
EDIT_NEW_TEXT = 4005       # 替换成的文字
BTN_RENAME_ALL = 5001      # 重命名所有对象
BTN_RENAME_SELECTED = 5002 # 重命名选中对象
BTN_CANCEL = 5003          # 取消按钮
class RenameDialog(gui.GeDialog):
    """重命名对话框类"""
    
    def CreateLayout(self):
        """创建对话框布局"""
        self.SetTitle("批量重命名工具")
        
        # 主要分组
        self.GroupBegin(GROUP_MAIN, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 1, 0)
        self.GroupBorderSpace(10, 10, 10, 10)
        
        # 重命名方式选择标题
        self.AddStaticText(0, c4d.BFH_LEFT, 0, 0, "选择重命名方式:", 0)
        self.AddSeparatorH(0, c4d.BFH_SCALEFIT)
        
        # 单选按钮组 - 确保互斥
        self.GroupBegin(GROUP_RADIO, c4d.BFH_SCALEFIT, 1, 0)
        
        # 方式1:序列号重命名
        self.AddRadioButton(RADIO_SEQUENCE, c4d.BFH_LEFT, 0, 0, "1. 序列号重命名")
        
        # 序列号重命名的参数
        self.GroupBegin(0, c4d.BFH_SCALEFIT, 2, 0)
        self.GroupBorderSpace(20, 5, 10, 5)
        self.AddStaticText(0, c4d.BFH_LEFT, 60, 0, "前缀:", 0)
        self.AddEditText(EDIT_PREFIX, c4d.BFH_SCALEFIT, 150, 0)
        self.AddStaticText(0, c4d.BFH_LEFT, 60, 0, "起始序号:", 0)
        self.AddEditNumberArrows(EDIT_START_NUM, c4d.BFH_LEFT, 80, 0)
        self.GroupEnd()
        
        # 分隔线
        self.AddSeparatorH(0, c4d.BFH_SCALEFIT)
        
        # 方式2:自定义重命名
        self.AddRadioButton(RADIO_CUSTOM, c4d.BFH_LEFT, 0, 0, "2. 自定义重命名")
        
        # 自定义重命名的参数
        self.GroupBegin(0, c4d.BFH_SCALEFIT, 2, 0)
        self.GroupBorderSpace(20, 5, 10, 5)
        self.AddStaticText(0, c4d.BFH_LEFT, 60, 0, "新名称:", 0)
        self.AddEditText(EDIT_CUSTOM_NAME, c4d.BFH_SCALEFIT, 150, 0)
        self.AddStaticText(0, c4d.BFH_LEFT, 0, 0, "注意:多个对象会自动添加序号", 0)
        self.GroupEnd()
        
        # 分隔线
        self.AddSeparatorH(0, c4d.BFH_SCALEFIT)
        
        # 方式3:字符替换
        self.AddRadioButton(RADIO_REPLACE, c4d.BFH_LEFT, 0, 0, "3. 字符替换")
        
        # 字符替换的参数
        self.GroupBegin(0, c4d.BFH_SCALEFIT, 2, 0)
        self.GroupBorderSpace(20, 5, 10, 5)
        self.AddStaticText(0, c4d.BFH_LEFT, 80, 0, "要替换的文字:", 0)
        self.AddEditText(EDIT_OLD_TEXT, c4d.BFH_SCALEFIT, 150, 0)
        self.AddStaticText(0, c4d.BFH_LEFT, 80, 0, "替换成:", 0)
        self.AddEditText(EDIT_NEW_TEXT, c4d.BFH_SCALEFIT, 150, 0)
        self.AddStaticText(0, c4d.BFH_LEFT, 0, 0, "注意:仅替换包含指定文字的对象名称", 0)
        self.GroupEnd()
        
        self.GroupEnd()  # 结束单选按钮组
        self.GroupEnd()  # 结束主分组
        
        # 分隔线
        self.AddSeparatorH(0, c4d.BFH_SCALEFIT)
        
        # 按钮组
        self.GroupBegin(GROUP_BUTTONS, c4d.BFH_CENTER, 3, 1)
        self.AddButton(BTN_RENAME_ALL, c4d.BFH_LEFT, 120, 0, "处理所有对象")
        self.AddButton(BTN_RENAME_SELECTED, c4d.BFH_LEFT, 120, 0, "处理选中对象")
        self.AddButton(BTN_CANCEL, c4d.BFH_LEFT, 80, 0, "取消")
        self.GroupEnd()
        
        return True
    
    def InitValues(self):
        """初始化控件值"""
        # 设置单选按钮组 - 默认选择序列号重命名
        self.SetBool(RADIO_SEQUENCE, True)
        self.SetBool(RADIO_CUSTOM, False)
        self.SetBool(RADIO_REPLACE, False)
        
        # 设置默认值
        self.SetString(EDIT_PREFIX, "对象_")
        self.SetInt32(EDIT_START_NUM, 1)
        self.SetString(EDIT_CUSTOM_NAME, "新对象")
        self.SetString(EDIT_OLD_TEXT, "")
        self.SetString(EDIT_NEW_TEXT, "")
        
        return True
    
    def Command(self, id, msg):
        """处理命令事件"""
        # 处理单选按钮互斥
        if id == RADIO_SEQUENCE:
            self.SetBool(RADIO_SEQUENCE, True)
            self.SetBool(RADIO_CUSTOM, False)
            self.SetBool(RADIO_REPLACE, False)
            
        elif id == RADIO_CUSTOM:
            self.SetBool(RADIO_SEQUENCE, False)
            self.SetBool(RADIO_CUSTOM, True)
            self.SetBool(RADIO_REPLACE, False)
            
        elif id == RADIO_REPLACE:
            self.SetBool(RADIO_SEQUENCE, False)
            self.SetBool(RADIO_CUSTOM, False)
            self.SetBool(RADIO_REPLACE, True)
            
        # 处理按钮点击
        elif id == BTN_RENAME_ALL:
            self.ProcessObjects(False)  # 处理所有对象
            
        elif id == BTN_RENAME_SELECTED:
            self.ProcessObjects(True)   # 处理选中对象
            
        elif id == BTN_CANCEL:
            self.Close()
            
        return True
    
    def ProcessObjects(self, selected_only):
        """处理对象 - 根据选择的模式执行不同操作"""
        doc = c4d.documents.GetActiveDocument()
        if not doc:
            gui.MessageDialog("没有活动的文档!")
            return
        
        # 获取要处理的对象列表
        if selected_only:
            objects = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
            if not objects:
                gui.MessageDialog("请先选择要处理的对象!")
                return
            scope_text = "选中"
        else:
            objects = self.GetAllObjects(doc.GetFirstObject())
            scope_text = "所有"
        
        if not objects:
            gui.MessageDialog("没有找到可处理的对象!")
            return
        
        # 开始撤销记录
        doc.StartUndo()
        
        try:
            # 根据选择的重命名方式进行处理
            if self.GetBool(RADIO_SEQUENCE):
                processed_count = self.RenameWithSequence(objects, doc)
                operation_text = "重命名"
            elif self.GetBool(RADIO_CUSTOM):
                processed_count = self.RenameWithCustomName(objects, doc)
                operation_text = "重命名"
            elif self.GetBool(RADIO_REPLACE):
                processed_count = self.ReplaceInObjects(objects, doc)
                operation_text = "替换"
            else:
                gui.MessageDialog("请选择一种处理方式!")
                doc.EndUndo()
                return
            
            # 结束撤销记录
            doc.EndUndo()
            
            # 刷新视图
            c4d.EventAdd()
            
            # 显示结果
            if processed_count > 0:
                gui.MessageDialog(f"成功{operation_text}了 {processed_count} 个对象!\n(从{scope_text}{len(objects)}个对象中筛选)")
            else:
                if self.GetBool(RADIO_REPLACE):
                    old_text = self.GetString(EDIT_OLD_TEXT)
                    gui.MessageDialog(f"在{scope_text}{len(objects)}个对象中没有找到包含'{old_text}'的对象名称!")
                else:
                    gui.MessageDialog("没有对象需要处理!")
            
        except Exception as e:
            doc.EndUndo()
            gui.MessageDialog(f"处理过程中出现错误:{str(e)}")
    
    def GetAllObjects(self, obj):
        """递归获取所有对象"""
        objects = []
        while obj:
            objects.append(obj)
            # 递归获取子对象
            if obj.GetDown():
                objects.extend(self.GetAllObjects(obj.GetDown()))
            obj = obj.GetNext()
        return objects
    
    def RenameWithSequence(self, objects, doc):
        """使用序列号重命名 - 重命名所有传入的对象"""
        prefix = self.GetString(EDIT_PREFIX)
        start_num = self.GetInt32(EDIT_START_NUM)
        
        processed_count = 0
        for i, obj in enumerate(objects):
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj)
            new_name = f"{prefix}{start_num + i:03d}"
            obj.SetName(new_name)
            processed_count += 1
            
        return processed_count
    
    def RenameWithCustomName(self, objects, doc):
        """使用自定义名称重命名 - 重命名所有传入的对象"""
        custom_name = self.GetString(EDIT_CUSTOM_NAME)
        
        if not custom_name.strip():
            gui.MessageDialog("请输入自定义名称!")
            return 0
        
        processed_count = 0
        if len(objects) == 1:
            # 只有一个对象时直接使用自定义名称
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, objects[0])
            objects[0].SetName(custom_name)
            processed_count = 1
        else:
            # 多个对象时添加序号
            for i, obj in enumerate(objects):
                doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj)
                new_name = f"{custom_name}_{i + 1:03d}"
                obj.SetName(new_name)
                processed_count += 1
                
        return processed_count
    
    def ReplaceInObjects(self, objects, doc):
        """字符替换功能 - 只替换包含指定字符的对象"""
        old_text = self.GetString(EDIT_OLD_TEXT)
        new_text = self.GetString(EDIT_NEW_TEXT)
        
        if not old_text.strip():
            gui.MessageDialog("请输入要替换的文字!")
            return 0
        
        processed_count = 0
        
        # 扫描所有对象,只处理包含指定文字的对象
        for obj in objects:
            current_name = obj.GetName()
            if old_text in current_name:
                doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj)
                new_name = current_name.replace(old_text, new_text)
                obj.SetName(new_name)
                processed_count += 1
                # 可以添加调试信息
                print(f"替换: '{current_name}' -> '{new_name}'")
        
        return processed_count
def main():
    """主函数"""
    dialog = RenameDialog()
    dialog.Open(c4d.DLG_TYPE_MODAL, defaultw=400, defaulth=500)
if __name__ == '__main__':
    main()