이번 글에서는 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`로 바꿉니다.
- 선택된 객체와 그 자손들을 모두 가져옵니다.
- 중복 객체를 제거하고 계층 구조에 따라 정렬합니다.
- 각 객체 이름에서 `find_prefix`를 찾아 `replace_with`로 바꿉니다.
- 이미 존재하는 이름이면 숫자를 붙여 고유한 이름을 만듭니다.
- `cmds.rename()` 명령을 사용하여 객체 이름을 실제로 변경합니다.
3. 스크립트 실행 및 사용법
- 첫번째 방법
- Maya Script Editor를 엽니다.
- ‘+’버튼(Add a new tab)을 누른 후 python을 선택하여 python창을 추가합니다.
- 위 코드를 Script Editor에 붙여넣습니다.
- 실행 버튼을 클릭하거나 `Ctrl+Enter`를 누릅니다.
- 두번째 방법
- Maya Script Editor를 엽니다.
- ‘+’버튼(Add a new tab)을 누른 후 python을 선택하여 python창을 추가합니다.
- 위 코드를 Script Editor에 붙여넣습니다.
- 붙여넣기된 전체 코드를 선택합니다.
- 선택된 코드 위에 마우스 커서를 가져간 후, 가운데 버튼을 누른채 드래그하여 ‘shelf’에 드랍합니다.
- 생성된 버튼을 우클릭하여 edit를 선택합니다.
- 열린 창에서 Shelves 탭으로 이동합니다.
- Tooltip에 적당한 설명을 작성해줍니다.
- Icon Label에 이름을 입력합니다.(예 : Renamer)
- 창의 가장 아래 ‘Save All Shelves’ 버튼을 클릭하여 저장합니다.
- 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)