見出し画像

[Blender][script]選択中のチャンネルでフィルターする(グラフエディタ、ドープシート)

グラフエディタとドープシートで選択中のチャンネルを検索フィルターに入力します。
複数オブジェクト選択対応。
複数チャンネル選択には非対応。最初に選択したチャンネルが適用されます。

メニュ―バーのビューから実行。日本語と英語でメニュ―が分かれています。

import bpy
import re

def clean_channel_name(name):
    """ 前後の [" "] を除去 """
    return re.sub(r'^\["(.*)"\]$', r'\1', name)

def get_channel_label(channel, lang="JP"):
    """ チャンネルの表示名を取得(日本語版と英語版) """
    path = channel.data_path
    index = channel.array_index
    xyz_labels = ["X", "Y", "Z", "W"]

    path_dict_jp = {
        "location": "位置",
        "rotation_euler": "オイラー角回転",
        "rotation_quaternion": "クォータニオン回転",
        "scale": "スケール",
        "delta_location": "Δ位置",
        "delta_rotation_euler": "Δオイラー角回転",
        "delta_scale": "Δスケール",
        "value": "値",
    }
    
    path_dict_eng = {
        "location": "Location",
        "rotation_euler": "Euler Rotation",
        "rotation_quaternion": "Quaternion Rotation",
        "scale": "Scale",
        "delta_location": "Δ Location",
        "delta_rotation_euler": "Δ Euler Rotation",
        "delta_scale": "Δ Scale",
        "value": "Value",
    }
    
    path_dict = path_dict_jp if lang == "JP" else path_dict_eng

    for key in path_dict.keys():
        if key in path:
            return f"{xyz_labels[index]} {path_dict[key]}"
    
    return clean_channel_name(path)

def set_channel_filter(lang="JP"):
    """ 最初に選択されたチャンネルを検索フィルターに設定(日本語版と英語版) """
    for area in bpy.context.screen.areas:
        if area.type in {'DOPESHEET_EDITOR', 'GRAPH_EDITOR'}:
            space = area.spaces.active
            if not hasattr(space, "dopesheet"):
                continue

            for obj in bpy.context.selected_objects:
                if obj.animation_data and obj.animation_data.action:
                    for fc in obj.animation_data.action.fcurves:
                        if fc.select:
                            channel_name = get_channel_label(fc, lang)
                            with bpy.context.temp_override(area=area):
                                space.dopesheet.filter_text = channel_name
                            print(f"Filter set: {channel_name}")
                            return
    print("No selected channels.")

def menu_func_jp(self, context):
    self.layout.operator("anim.set_channel_filter_jp", text="Filter by Selected Channel(JP)")

def menu_func_eng(self, context):
    self.layout.operator("anim.set_channel_filter_eng", text="Filter by Selected Channel(ENG)")

class ANIM_OT_SetChannelFilterJP(bpy.types.Operator):
    """選択したチャンネルを基準にフィルターを適用"""
    bl_idname = "anim.set_channel_filter_jp"
    bl_label = "Filter by Selected Channel(JP)"

    def execute(self, context):
        set_channel_filter("JP")
        return {'FINISHED'}

class ANIM_OT_SetChannelFilterENG(bpy.types.Operator):
    """Apply filter based on selected channel"""
    bl_idname = "anim.set_channel_filter_eng"
    bl_label = "Filter by Selected Channel(ENG)"

    def execute(self, context):
        set_channel_filter("ENG")
        return {'FINISHED'}

def register():
    bpy.utils.register_class(ANIM_OT_SetChannelFilterJP)
    bpy.utils.register_class(ANIM_OT_SetChannelFilterENG)
    bpy.types.DOPESHEET_MT_view.append(menu_func_jp)
    bpy.types.DOPESHEET_MT_view.append(menu_func_eng)
    bpy.types.GRAPH_MT_view.append(menu_func_jp)
    bpy.types.GRAPH_MT_view.append(menu_func_eng)

def unregister():
    bpy.utils.unregister_class(ANIM_OT_SetChannelFilterJP)
    bpy.utils.unregister_class(ANIM_OT_SetChannelFilterENG)
    bpy.types.DOPESHEET_MT_view.remove(menu_func_jp)
    bpy.types.DOPESHEET_MT_view.remove(menu_func_eng)
    bpy.types.GRAPH_MT_view.remove(menu_func_jp)
    bpy.types.GRAPH_MT_view.remove(menu_func_eng)

if __name__ == "__main__":
    register()

いいなと思ったら応援しよう!