Maya에서 Python으로 선택한 객체들의 이름 바꾸기: Maya_Bulk_Renamer_v1.0.py

Maya에서 Python으로 선택한 객체들의 이름 바꾸기: Maya_Bulk_Renamer_v1.0.py | 지식에 대한 탐구

이번 글에서는 Maya에서 Python 스크립트를 사용하여 객체 이름을 바꾸는 고급 툴을 만드는 방법을 알아봅니다. 이 툴은 다양한 이름 변경 기능을 제공하며, 초보자도 쉽게 사용할 수 있도록 UI까지 구현합니다.

1. 필요한 도구

  • Autodesk Maya: 3D 모델링 및 애니메이션 소프트웨어
  • Python: 스크립트 작성 및 실행에 사용되는 프로그래밍 언어 (Maya에 내장)

2. 스크립트 분석

제공된 스크립트를 기능별로 나누어 살펴보겠습니다.

2.1. 기본 함수

import maya.cmds as cmds  # Maya 명령어를 사용할 수 있도록 cmds 모듈을 임포트
import re  # 정규 표현식을 사용하기 위해 re 모듈을 임포트

# 주어진 객체와 모든 자손 객체를 반환하는 함수
def get_all_descendants(obj):
    """객체와 그 모든 자손을 가져오는 함수"""
    descendants = cmds.listRelatives(obj, allDescendents=True, fullPath=True) or []
    return [obj] + descendants

# 선택된 에셋의 이름을 변경하는 함수
def rename_selected_assets(find_prefix, replace_with):
    selected_assets = cmds.ls(selection=True, long=True)  # 현재 선택된 오브젝트를 가져옴
    all_objects_to_rename = []

    # 모든 선택된 에셋과 그 자손들을 가져오기
    for asset in selected_assets:
        all_objects_to_rename.extend(get_all_descendants(asset))
    
    # 중복 제거
    all_objects_to_rename = list(set(all_objects_to_rename))
    
    # 이름 기반으로 정렬하여 자식이 부모보다 먼저 오지 않도록 함
    all_objects_to_rename.sort(key=lambda x: x.count('|'), reverse=True)

    for obj in all_objects_to_rename:
        if not cmds.objExists(obj):
            continue  # 오브젝트가 존재하지 않으면 건너뛰기

        relative_name = obj.split('|')[-1]  # 오브젝트의 이름만 추출
        if find_prefix in relative_name:
            new_name_base = relative_name.replace(find_prefix, replace_with)  # 이름에서 찾을 접두사를 대체할 접두사로 변경
            new_name_base = re.sub('[^0-9a-zA-Z_]+', '', new_name_base)  # 알파벳과 숫자, 언더스코어를 제외한 모든 문자를 제거

            new_name = new_name_base
            increment = 1
            while cmds.objExists(new_name):  # 같은 이름의 오브젝트가 존재하면 숫자를 추가하여 고유한 이름을 생성
                new_name = f"{new_name_base}{increment}"
                increment += 1

            # 실제 리네임 수행
            cmds.rename(obj, new_name)
            print(f"Renamed: {obj} to {new_name}")

    print("Selected assets rename operation completed.")
  • `maya.cmds` 모듈은 Maya 명령어에 접근하기 위해 가져옵니다.
  • `re` 모듈은 정규 표현식을 사용하여 문자열을 조작하기 위해 가져옵니다.
  • `get_all_descendants(obj)` 함수는 주어진 객체와 그 모든 자손 객체를 리스트로 반환합니다.
  • `rename_selected_assets(find_prefix, replace_with)` 함수는 선택된 객체들의 이름을 변경합니다. (자세한 내용은 뒤에서 설명)

2.2. 이름 변경 함수

# 시리즈로 이름을 변경하는 함수
def rename_series(name, start_number, padding):
    selected_assets = cmds.ls(selection=True)  # 현재 선택된 오브젝트를 가져옴
    for i, asset in enumerate(selected_assets, start=start_number):
        new_name = f"{name}{str(i).zfill(padding)}"  # 새 이름 생성 (패딩 적용)
        cmds.rename(asset, new_name)
    print("시리즈로 이름 변경 완료")

# Prefix를 추가하는 함수
def add_prefix(prefix):
    selected_assets = cmds.ls(selection=True)  # 현재 선택된 오브젝트를 가져옴
    for asset in selected_assets:
        new_name = prefix + asset  # 접두사를 추가하여 새 이름 생성
        cmds.rename(asset, new_name)
    print("Prefix 추가 완료")

# Subfix를 추가하는 함수
def add_subfix(subfix):
    selected_assets = cmds.ls(selection=True)  # 현재 선택된 오브젝트를 가져옴
    for asset in selected_assets:
        new_name = asset + subfix  # 접미사를 추가하여 새 이름 생성
        cmds.rename(asset, new_name)
    print("Subfix 추가 완료")

# 특정 Subfix를 추가하는 함수
def add_specific_subfix(subfix):
    selected_assets = cmds.ls(selection=True, long=True)  # 현재 선택된 오브젝트를 가져옴
    all_objects = {}
    # 객체와 그 자손들을 가져오면서 타입도 함께 저장
    for asset in selected_assets:
        descendants = cmds.listRelatives(asset, allDescendents=True, fullPath=True, type='transform') or []
        all_objects[asset] = cmds.objectType(asset)
        for desc in descendants:
            all_objects[desc] = cmds.objectType(desc)

    # 계층 구조에 따라 정렬하여 부모 객체가 먼저 오도록 함
    all_objects_sorted = sorted(all_objects.keys(), key=lambda x: x.count('|'), reverse=True)

    for obj in all_objects_sorted:
        obj_type = all_objects[obj]
        short_name = obj.split('|')[-1]  # 실제 객체의 이름만 추출
        # 타입에 따라 접미사 적용
        if subfix == 'grp' and obj_type == 'transform':
            children = cmds.listRelatives(obj, children=True) or []
            # 자식이 있는 transform만 그룹으로 간주
            if any(cmds.objectType(c) != 'transform' for c in children):
                new_name_suffix = f"{short_name}_{subfix}"  # 새 이름 생성
                cmds.rename(obj, new_name_suffix)
                print(f"Renamed: {obj} to {new_name_suffix}")
        elif subfix == 'geo' and obj_type == 'mesh':
            parent = cmds.listRelatives(obj, parent=True, fullPath=True) or []
            if parent:
                parent_short_name = parent[0].split('|')[-1]
                new_name_suffix = f"{parent_short_name}_{subfix}"
                cmds.rename(parent[0], new_name_suffix)
                print(f"Renamed: {parent[0]} to {new_name_suffix}")
        elif subfix == 'jnt' and obj_type == 'joint':
            new_name_suffix = f"{short_name}_{subfix}"
            cmds.rename(obj, new_name_suffix)
            print(f"Renamed: {obj} to {new_name_suffix}")
        else:
            print(f"Skipping {obj}: Not applicable for '{subfix}' subfix.")

    print(f"{subfix} Subfix 추가 완료")
  • `rename_series(name, start_number, padding)` 함수는 선택된 객체들을 시리즈 형태로 이름을 변경합니다. 예: name_01, name_02, …
  • `add_prefix(prefix)` 함수는 선택된 객체들에 prefix를 추가합니다.
  • `add_subfix(subfix)` 함수는 선택된 객체들에 subfix를 추가합니다.
  • `add_specific_subfix(subfix)` 함수는 선택된 객체들의 타입에 따라 특정 subfix를 추가합니다. 예: _grp, _geo, _jnt 등.

2.3. UI 생성 함수

# UI 생성 함수
def create_ui():
    window_name = "custom_rename_ui"  # 창의 이름 설정
    if cmds.window(window_name, exists=True):  # 같은 이름의 창이 존재하는지 확인
        cmds.deleteUI(window_name)  # 존재하면 삭제
    
    cmds.window(window_name, title="Advanced Rename Tool", widthHeight=(400, 300))  # 새로운 창 생성
    cmds.columnLayout(adjustableColumn=True)  # 칼럼 레이아웃 생성

    # 찾기/바꾸기 기능
    cmds.textFieldGrp('prefix_field', label='Find Prefix', text='')  # 찾을 접두사 입력 필드
    cmds.textFieldGrp('new_prefix_field', label='Replace With', text='')  # 바꿀 접두사 입력 필드
    cmds.button(label="Rename Selected Assets", command=lambda x: rename_selected_assets(cmds.textFieldGrp('prefix_field', query=True, text=True), cmds.textFieldGrp('new_prefix_field', query=True, text=True)))  # 버튼 클릭 시 rename_selected_assets 함수 호출

    # 시리즈 이름 변경
    cmds.textFieldGrp('series_name', label='Series Name', text='name_')  # 시리즈 이름 입력 필드
    cmds.intFieldGrp('series_start_number', label='Start Number', value1=1)  # 시작 번호 입력 필드
    cmds.intFieldGrp('series_padding', label='Number Padding', value1=2)  # 번호 패딩 입력 필드
    cmds.button(label="Rename Series", command=lambda x: rename_series(cmds.textFieldGrp('series_name', query=True, text=True), cmds.intFieldGrp('series_start_number', query=True, value1=True), cmds.intFieldGrp('series_padding', query=True, value1=True)))  # 버튼 클릭 시 rename_series 함수 호출

    # Prefix 추가
    cmds.textFieldGrp('add_prefix_field', label='Add Prefix', text='prefix_')  # 접두사 입력 필드
    cmds.button(label="Add Prefix", command=lambda x: add_prefix(cmds.textFieldGrp('add_prefix_field', query=True, text=True)))  # 버튼 클릭 시 add_prefix 함수 호출

    # Subfix 추가
    cmds.textFieldGrp('subfix_field', label='Add Subfix', text='_subfix')  # 접미사 입력 필드
    cmds.button(label="Add Subfix", command=lambda x: add_subfix(cmds.textFieldGrp('subfix_field', query=True, text=True)))  # 버튼 클릭 시 add_subfix 함수 호출

    cmds.showWindow(window_name)  # 창을 화면에 표시

# UI 생성 함수 호출
create_ui()

이 함수는 선택된 객체들의 이름에서 `find_prefix`를 찾아 `replace_with`로 바꿉니다.

  1. 선택된 객체와 그 자손들을 모두 가져옵니다.
  2. 중복 객체를 제거하고 계층 구조에 따라 정렬합니다.
  3. 각 객체 이름에서 `find_prefix`를 찾아 `replace_with`로 바꿉니다.
  4. 이미 존재하는 이름이면 숫자를 붙여 고유한 이름을 만듭니다.
  5. `cmds.rename()` 명령을 사용하여 객체 이름을 실제로 변경합니다.

 







 

3. 스크립트 실행 및 사용법

    첫번째 방법

  1. Maya Script Editor를 엽니다.
  2. ‘+’버튼(Add a new tab)을 누른 후 python을 선택하여 python창을 추가합니다.
  3. 위 코드를 Script Editor에 붙여넣습니다.
  4. 실행 버튼을 클릭하거나 `Ctrl+Enter`를 누릅니다.
    두번째 방법

  1. Maya Script Editor를 엽니다.
  2. ‘+’버튼(Add a new tab)을 누른 후 python을 선택하여 python창을 추가합니다.
  3. 위 코드를 Script Editor에 붙여넣습니다.
  4. 붙여넣기된 전체 코드를 선택합니다.
  5. 선택된 코드 위에 마우스 커서를 가져간 후, 가운데 버튼을 누른채 드래그하여 ‘shelf’에 드랍합니다.
  6. 생성된 버튼을 우클릭하여 edit를 선택합니다.
  7. 열린 창에서 Shelves 탭으로 이동합니다.
  8. Tooltip에 적당한 설명을 작성해줍니다.
  9. Icon Label에 이름을 입력합니다.(예 : Renamer)
  10. 창의 가장 아래 ‘Save All Shelves’ 버튼을 클릭하여 저장합니다.
  11. Rename 버튼을 클릭하여 실행합니다.

이제 Maya UI에 “Advanced Rename Tool” 창이 나타납니다. 각 기능에 해당하는 입력 필드에 값을 입력하고 버튼을 클릭하면 객체들의 이름을 변경할 수 있습니다.

4. 전체 코드

import maya.cmds as cmds  # Maya 명령어를 사용할 수 있도록 cmds 모듈을 임포트
import re  # 정규 표현식을 사용하기 위해 re 모듈을 임포트

# 주어진 객체와 모든 자손 객체를 반환하는 함수
def get_all_descendants(obj):
    """객체와 그 모든 자손을 가져오는 함수"""
    descendants = cmds.listRelatives(obj, allDescendents=True, fullPath=True) or []
    return [obj] + descendants

# 선택된 에셋의 이름을 변경하는 함수
def rename_selected_assets(find_prefix, replace_with):
    selected_assets = cmds.ls(selection=True, long=True)  # 현재 선택된 오브젝트를 가져옴
    all_objects_to_rename = []

    # 모든 선택된 에셋과 그 자손들을 가져오기
    for asset in selected_assets:
        all_objects_to_rename.extend(get_all_descendants(asset))
    
    # 중복 제거
    all_objects_to_rename = list(set(all_objects_to_rename))
    
    # 이름 기반으로 정렬하여 자식이 부모보다 먼저 오지 않도록 함
    all_objects_to_rename.sort(key=lambda x: x.count('|'), reverse=True)

    for obj in all_objects_to_rename:
        if not cmds.objExists(obj):
            continue  # 오브젝트가 존재하지 않으면 건너뛰기

        relative_name = obj.split('|')[-1]  # 오브젝트의 이름만 추출
        if find_prefix in relative_name:
            new_name_base = relative_name.replace(find_prefix, replace_with)  # 이름에서 찾을 접두사를 대체할 접두사로 변경
            new_name_base = re.sub('[^0-9a-zA-Z_]+', '', new_name_base)  # 알파벳과 숫자, 언더스코어를 제외한 모든 문자를 제거

            new_name = new_name_base
            increment = 1
            while cmds.objExists(new_name):  # 같은 이름의 오브젝트가 존재하면 숫자를 추가하여 고유한 이름을 생성
                new_name = f"{new_name_base}{increment}"
                increment += 1

            # 실제 리네임 수행
            cmds.rename(obj, new_name)
            print(f"Renamed: {obj} to {new_name}")

    print("Selected assets rename operation completed.")

# 시리즈로 이름을 변경하는 함수
def rename_series(name, start_number, padding):
    selected_assets = cmds.ls(selection=True)  # 현재 선택된 오브젝트를 가져옴
    for i, asset in enumerate(selected_assets, start=start_number):
        new_name = f"{name}{str(i).zfill(padding)}"  # 새 이름 생성 (패딩 적용)
        cmds.rename(asset, new_name)
    print("시리즈로 이름 변경 완료")

# Prefix를 추가하는 함수
def add_prefix(prefix):
    selected_assets = cmds.ls(selection=True)  # 현재 선택된 오브젝트를 가져옴
    for asset in selected_assets:
        new_name = prefix + asset  # 접두사를 추가하여 새 이름 생성
        cmds.rename(asset, new_name)
    print("Prefix 추가 완료")

# Subfix를 추가하는 함수
def add_subfix(subfix):
    selected_assets = cmds.ls(selection=True)  # 현재 선택된 오브젝트를 가져옴
    for asset in selected_assets:
        new_name = asset + subfix  # 접미사를 추가하여 새 이름 생성
        cmds.rename(asset, new_name)
    print("Subfix 추가 완료")

# 특정 Subfix를 추가하는 함수
def add_specific_subfix(subfix):
    selected_assets = cmds.ls(selection=True, long=True)  # 현재 선택된 오브젝트를 가져옴
    all_objects = {}
    # 객체와 그 자손들을 가져오면서 타입도 함께 저장
    for asset in selected_assets:
        descendants = cmds.listRelatives(asset, allDescendents=True, fullPath=True, type='transform') or []
        all_objects[asset] = cmds.objectType(asset)
        for desc in descendants:
            all_objects[desc] = cmds.objectType(desc)

    # 계층 구조에 따라 정렬하여 부모 객체가 먼저 오도록 함
    all_objects_sorted = sorted(all_objects.keys(), key=lambda x: x.count('|'), reverse=True)

    for obj in all_objects_sorted:
        obj_type = all_objects[obj]
        short_name = obj.split('|')[-1]  # 실제 객체의 이름만 추출
        # 타입에 따라 접미사 적용
        if subfix == 'grp' and obj_type == 'transform':
            children = cmds.listRelatives(obj, children=True) or []
            # 자식이 있는 transform만 그룹으로 간주
            if any(cmds.objectType(c) != 'transform' for c in children):
                new_name_suffix = f"{short_name}_{subfix}"  # 새 이름 생성
                cmds.rename(obj, new_name_suffix)
                print(f"Renamed: {obj} to {new_name_suffix}")
        elif subfix == 'geo' and obj_type == 'mesh':
            parent = cmds.listRelatives(obj, parent=True, fullPath=True) or []
            if parent:
                parent_short_name = parent[0].split('|')[-1]
                new_name_suffix = f"{parent_short_name}_{subfix}"
                cmds.rename(parent[0], new_name_suffix)
                print(f"Renamed: {parent[0]} to {new_name_suffix}")
        elif subfix == 'jnt' and obj_type == 'joint':
            new_name_suffix = f"{short_name}_{subfix}"
            cmds.rename(obj, new_name_suffix)
            print(f"Renamed: {obj} to {new_name_suffix}")
        else:
            print(f"Skipping {obj}: Not applicable for '{subfix}' subfix.")

    print(f"{subfix} Subfix 추가 완료")

# UI 생성 함수
def create_ui():
    window_name = "custom_rename_ui"  # 창의 이름 설정
    if cmds.window(window_name, exists=True):  # 같은 이름의 창이 존재하는지 확인
        cmds.deleteUI(window_name)  # 존재하면 삭제
    
    cmds.window(window_name, title="Advanced Rename Tool", widthHeight=(400, 300))  # 새로운 창 생성
    cmds.columnLayout(adjustableColumn=True)  # 칼럼 레이아웃 생성

    # 찾기/바꾸기 기능
    cmds.textFieldGrp('prefix_field', label='Find Prefix', text='')  # 찾을 접두사 입력 필드
    cmds.textFieldGrp('new_prefix_field', label='Replace With', text='')  # 바꿀 접두사 입력 필드
    cmds.button(label="Rename Selected Assets", command=lambda x: rename_selected_assets(cmds.textFieldGrp('prefix_field', query=True, text=True), cmds.textFieldGrp('new_prefix_field', query=True, text=True)))  # 버튼 클릭 시 rename_selected_assets 함수 호출

    # 시리즈 이름 변경
    cmds.textFieldGrp('series_name', label='Series Name', text='name_')  # 시리즈 이름 입력 필드
    cmds.intFieldGrp('series_start_number', label='Start Number', value1=1)  # 시작 번호 입력 필드
    cmds.intFieldGrp('series_padding', label='Number Padding', value1=2)  # 번호 패딩 입력 필드
    cmds.button(label="Rename Series", command=lambda x: rename_series(cmds.textFieldGrp('series_name', query=True, text=True), cmds.intFieldGrp('series_start_number', query=True, value1=True), cmds.intFieldGrp('series_padding', query=True, value1=True)))  # 버튼 클릭 시 rename_series 함수 호출

    # Prefix 추가
    cmds.textFieldGrp('add_prefix_field', label='Add Prefix', text='prefix_')  # 접두사 입력 필드
    cmds.button(label="Add Prefix", command=lambda x: add_prefix(cmds.textFieldGrp('add_prefix_field', query=True, text=True)))  # 버튼 클릭 시 add_prefix 함수 호출

    # Subfix 추가
    cmds.textFieldGrp('subfix_field', label='Add Subfix', text='_subfix')  # 접미사 입력 필드
    cmds.button(label="Add Subfix", command=lambda x: add_subfix(cmds.textFieldGrp('subfix_field', query=True, text=True)))  # 버튼 클릭 시 add_subfix 함수 호출

    cmds.showWindow(window_name)  # 창을 화면에 표시

# UI 생성 함수 호출
create_ui()

 

6. 이 코드에서 사용된 Maya python 함수들

🔗cmds.window

주어진 이름과 속성으로 새 창을 생성합니다.

cmds.window(window_name, title="Advanced Rename Tool", widthHeight=(400, 300))

🔗cmds.columnLayout

수직으로 정렬된 레이아웃을 생성합니다.

cmds.columnLayout(adjustableColumn=True)

🔗cmds.textFieldGrp

라벨과 텍스트 입력 필드가 함께 있는 UI 요소를 생성합니다.

cmds.textFieldGrp('prefix_field', label='Find Prefix', text='')

🔗cmds.button

버튼 UI 요소를 생성합니다. 클릭 시 연결된 명령어를 실행합니다.

cmds.button(label="Rename Selected Assets", command=lambda x: rename_selected_assets(cmds.textFieldGrp('prefix_field', query=True, text=True), cmds.textFieldGrp('new_prefix_field', query=True, text=True)))

🔗cmds.showWindow

지정된 창을 화면에 표시합니다.

cmds.showWindow(window_name)

🔗cmds.listRelatives

지정된 오브젝트의 자식, 부모, 형제 등 관련 오브젝트를 나열합니다.

cmds.listRelatives(obj, allDescendents=True, fullPath=True)

🔗cmds.ls

선택된 오브젝트나 특정 조건을 만족하는 오브젝트를 나열합니다.

cmds.ls(selection=True, long=True)

🔗cmds.objExists

지정된 오브젝트가 현재 장면에 존재하는지 확인합니다.

cmds.objExists(obj)

🔗cmds.rename

지정된 오브젝트의 이름을 변경합니다.

cmds.rename(obj, new_name)

🔗cmds.intFieldGrp

라벨과 정수 입력 필드가 함께 있는 UI 요소를 생성합니다.

cmds.intFieldGrp('series_start_number', label='Start Number', value1=1)

🔗cmds.deleteUI

지정된 UI 요소를 삭제합니다.

cmds.deleteUI(window_name)