Mayaのレンダーレイヤーをリネームするスクリプト

## RenderLayer一括リネームツール
Autodesk MayaのRender Layerの名前をリネームするpython scriptを作りました。
Render Settings > Image File Outputで<layer>の前後になにかつけることで出力パスやファイル名を変更することは可能ですが、一意性を保つためにRenderLayer自体をリネームしたほうが良さそうと考え作りました。

## 主な機能
- UIによる操作性
    * スクリプト実行時に表示されるウィンドウで、レンダーレイヤー一覧がテキストスクロールリストとして表示され、選択して使用します。

- 柔軟な名称変更オプション
    * 検索＆置換: 指定した文字列を置換して名前を変更
    * 接頭辞・接尾辞の追加: 名前の前後に任意の文字列を付加
    * 連番の付加: 数値またはアルファベット形式で一意の連番を追加
## 使い方
1. UIウィンドウの起動:
スクリプトを実行すると、「RenderLayer一括リネームツール」のウィンドウが表示され、現行のRenderLayerが一覧化されます。

1. オプション設定:
各名称変更オプション（検索＆置換、接頭辞・接尾辞、連番追加）をチェックし、必要な文字列や連番の開始値を入力します。

1. リネーム実行:
対象のレンダーレイヤーを選択し、「リネーム実行」ボタンをクリックすると、設定に従って名前が更新されます。

## UI
![renameRenderLayers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134079/2806bf98-ca2a-4175-96db-11a7b070e443.png)


## コード
```python
import maya.cmds as cmds
import maya.app.renderSetup.model.renderSetup as renderSetup

def get_non_default_render_layers():
    
    rs = renderSetup.instance()
    all_layers = rs.getRenderLayers()
    non_default_layers = [layer for layer in all_layers if layer.name() != "defaultRenderLayer"]
    return non_default_layers

def update_layer_list(list_widget):
   
    cmds.textScrollList(list_widget, edit=True, removeAll=True)
    layers = get_non_default_render_layers()
    for layer in layers:
        cmds.textScrollList(list_widget, edit=True, append=layer.name())

def refresh_layer_list(*args):
    
    update_layer_list('renderLayerList')
    cmds.inViewMessage(amg='レンダーレイヤーリストを更新しました。', pos='midCenter', fade=True)

def rename_layers(*args):
    
    selected_layers = cmds.textScrollList('renderLayerList', query=True, selectItem=True)
    if not selected_layers:
        cmds.warning("リネームするレンダーレイヤーが選択されていません。")
        return

    enable_search_replace = cmds.checkBox('chk_search_replace', query=True, value=True)
    search_str = cmds.textField('txt_search', query=True, text=True) if enable_search_replace else ""
    replace_str = cmds.textField('txt_replace', query=True, text=True) if enable_search_replace else ""

    enable_prefix = cmds.checkBox('chk_prefix', query=True, value=True)
    prefix_str = cmds.textField('txt_prefix', query=True, text=True) if enable_prefix else ""

    enable_suffix = cmds.checkBox('chk_suffix', query=True, value=True)
    suffix_str = cmds.textField('txt_suffix', query=True, text=True) if enable_suffix else ""

    enable_serial = cmds.checkBox('chk_serial', query=True, value=True)

    rs = renderSetup.instance()
    all_layers = rs.getRenderLayers()
    layer_dict = { layer.name(): layer for layer in all_layers if layer.name() != "defaultRenderLayer" }

    serial_num = 0
    if enable_serial:
        if cmds.radioButton('radio_alphabet', query=True, select=True):
            alphabet_start = cmds.textField('txt_alphabet_start', query=True, text=True).strip().upper()
            if len(alphabet_start) != 1 or not alphabet_start.isalpha():
                cmds.warning("アルファベットの開始文字が不正です。'A'を使用します。")
                alphabet_start = "A"
            base_ord = ord(alphabet_start)
        else:
            serial_start = cmds.intField('int_serial', query=True, value=True)
            serial_num = serial_start

    for old_name in selected_layers:
        if old_name not in layer_dict:
            cmds.warning("レンダーレイヤー {} が見つかりません。".format(old_name))
            continue
        layer_obj = layer_dict[old_name]
        new_name = old_name

        if enable_search_replace and search_str:
            new_name = new_name.replace(search_str, replace_str)
        if enable_prefix:
            new_name = prefix_str + new_name
        if enable_suffix:
            new_name = new_name + suffix_str
        if enable_serial:
            if cmds.radioButton('radio_alphabet', query=True, select=True):
                index = serial_num
                if index + base_ord > ord('Z'):
                    cmds.warning("アルファベット連番はAからZまでしか対応していません。'Z'を適用します。")
                    letter = "Z"
                else:
                    letter = chr(base_ord + index)
                new_name = "{}_{}".format(new_name, letter)
                serial_num += 1
            else:
                new_name = "{}_{:03d}".format(new_name, serial_num)
                serial_num += 1

        try:
            layer_obj.setName(new_name)
            print("レンダーレイヤー「{}」を「{}」に変更しました。".format(old_name, new_name))
        except Exception as e:
            cmds.warning("リネームに失敗しました: {}: {}".format(old_name, str(e)))

    update_layer_list('renderLayerList')

def create_ui():

    win_name = "RenderLayerRenameWindow"
    if cmds.window(win_name, exists=True):
        cmds.deleteUI(win_name)
    window = cmds.window(win_name, title="RenderLayer一括リネームツール", widthHeight=(400, 650))
    cmds.columnLayout(adjustableColumn=True)

    cmds.text(label="対象のレンダーレイヤーを選択してください（defaultレイヤーは除外）", align="center")
    cmds.textScrollList('renderLayerList', allowMultiSelection=True, height=150)
    cmds.button(label="リフレッシュ", command=refresh_layer_list)
    update_layer_list('renderLayerList')
    cmds.separator(height=10, style="in")

    cmds.frameLayout(label="検索＆置換", collapsable=True, collapse=False)
    cmds.columnLayout(adjustableColumn=True)
    cmds.checkBox('chk_search_replace', label="検索＆置換を有効にする", value=False)
    cmds.rowLayout(numberOfColumns=2, adjustableColumn=1)
    cmds.text(label="検索文字列:", align="left")
    cmds.textField('txt_search')
    cmds.setParent('..')
    cmds.rowLayout(numberOfColumns=2, adjustableColumn=1)
    cmds.text(label="置換文字列:", align="left")
    cmds.textField('txt_replace')
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.separator(height=10, style="in")

    cmds.frameLayout(label="接頭辞追加", collapsable=True, collapse=False)
    cmds.columnLayout(adjustableColumn=True)
    cmds.checkBox('chk_prefix', label="接頭辞追加を有効にする", value=False)
    cmds.rowLayout(numberOfColumns=2, adjustableColumn=1)
    cmds.text(label="接頭辞:", align="left")
    cmds.textField('txt_prefix')
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.separator(height=10, style="in")

    cmds.frameLayout(label="接尾辞追加", collapsable=True, collapse=False)
    cmds.columnLayout(adjustableColumn=True)
    cmds.checkBox('chk_suffix', label="接尾辞追加を有効にする", value=False)
    cmds.rowLayout(numberOfColumns=2, adjustableColumn=1)
    cmds.text(label="接尾辞:", align="left")
    cmds.textField('txt_suffix')
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.separator(height=10, style="in")

    cmds.frameLayout(label="連番追加", collapsable=True, collapse=False)
    cmds.columnLayout(adjustableColumn=True)
    cmds.checkBox('chk_serial', label="連番追加を有効にする", value=False)
    cmds.separator(height=5, style="none")
    cmds.text(label="連番形式", align="left")
    cmds.radioCollection('radio_serial_type')
    cmds.radioButton('radio_numeric', label="数字", select=True)
    cmds.rowLayout(numberOfColumns=2, adjustableColumn=1)
    cmds.text(label="開始番号:", align="left")
    cmds.intField('int_serial', value=1)
    cmds.setParent('..')
    cmds.separator(height=5, style="none")
    cmds.radioButton('radio_alphabet', label="アルファベット")
    cmds.rowLayout(numberOfColumns=2, adjustableColumn=1)
    cmds.text(label="開始文字:", align="left")
    cmds.textField('txt_alphabet_start', text="A")
    cmds.setParent('..')
    cmds.setParent('..')
    cmds.separator(height=10, style="in")

    cmds.button(label="リネーム実行", command=rename_layers)
    cmds.showWindow(window)

create_ui()


```