Skip to content
Snippets Groups Projects
ui.py 51.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    
    import bpy
    
    from bpy.props import (
        BoolProperty,
        IntProperty,
        EnumProperty,
        StringProperty
    )
    
    
    from .utils.errors import MetarigError
    from .utils.rig import write_metarig
    from .utils.widgets import write_widget
    from .utils.naming import unique_name
    from .utils.rig import upgradeMetarigTypes, outdated_types
    
    
    from .rigs.utils import get_limb_generated_names
    
    
    from .utils.animation import get_keyed_frames_in_range, bones_in_frame, overwrite_prop_animation
    from .utils.animation import RIGIFY_OT_get_frame_range
    
    from .utils.animation import register as animation_register
    from .utils.animation import unregister as animation_unregister
    
    from . import base_rig
    
    from . import rig_lists
    from . import generate
    
    
    
    def build_type_list(context, rigify_types):
        rigify_types.clear()
    
        for r in sorted(rig_lists.rigs):
            if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
    
                    or len(feature_set_list.get_enabled_modules_names()) == 0
    
    class DATA_PT_rigify(bpy.types.Panel):
        bl_label = "Rigify"
    
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "data"
    
    
        @classmethod
        def poll(cls, context):
    
            obj = context.object
    
            if not context.object:
                return False
    
            return obj.type == 'ARMATURE' \
    
                and obj.data.get("rig_id") is None
    
    
        def draw(self, context):
    
            layout = self.layout
    
            WARNING = "Warning: Some features may change after generation"
            show_warning = False
            show_update_metarig = False
            show_not_updatable = False
            show_upgrade_face = False
    
            check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
    
            for posebone in obj.pose.bones:
                bone = posebone.bone
                if not bone:
                    # If we are in edit mode and the bone was just created,
                    # a pose bone won't exist yet.
                    continue
                if bone.layers[30] and (list(set(posebone.keys()) & set(check_props))):
                    show_warning = True
                    break
    
            for b in obj.pose.bones:
                if b.rigify_type in outdated_types.keys():
                    old_bone = b.name
                    old_rig = b.rigify_type
                    if outdated_types[b.rigify_type]:
                        show_update_metarig = True
                    else:
                        show_update_metarig = False
                        show_not_updatable = True
    
                elif b.rigify_type == 'faces.super_face':
                    show_upgrade_face = True
    
            if show_warning:
                layout.label(text=WARNING, icon='ERROR')
    
            enable_generate = not (show_not_updatable or show_update_metarig)
    
            if show_not_updatable:
                layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and cannot be upgraded automatically.", icon='ERROR')
                layout.label(text="("+old_rig+" on bone "+old_bone+")")
            elif show_update_metarig:
                layout.label(text="This metarig contains old rig-types that can be automatically upgraded to benefit of rigify's new features.", icon='ERROR')
                layout.label(text="("+old_rig+" on bone "+old_bone+")")
                layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
            elif show_upgrade_face:
                layout.label(text="This metarig uses the old face rig.", icon='INFO')
                layout.operator("pose.rigify_upgrade_face")
    
            row = layout.row()
            # Rig type field
    
            col = layout.column(align=True)
            col.active = (not 'rig_id' in C.object.data)
    
            col.separator()
            row = col.row()
            text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
            row.operator("pose.rigify_generate", text=text, icon='POSE_HLT')
            row.enabled = enable_generate
    
    
    class DATA_PT_rigify_advanced(bpy.types.Panel):
    
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "data"
        bl_label = "Advanced"
    
        bl_parent_id = 'DATA_PT_rigify'
    
        bl_options = {'DEFAULT_CLOSED'}
    
        def draw(self, context):
            layout = self.layout
            layout.use_property_split = True
            layout.use_property_decorate = False
    
            armature_id_store = context.object.data
    
            col = layout.column()
    
    
            row = col.row()
            row.active = not armature_id_store.rigify_target_rig
            row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name")
    
            col.separator()
    
    
            col2 = col.box().column()
            col2.label(text="Overwrite Existing:")
            col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig")
            col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script")
            col2.row().prop(armature_id_store, "rigify_widgets_collection")
    
    
            col.row().prop(armature_id_store, "rigify_force_widget_update")
            col.row().prop(armature_id_store, "rigify_mirror_widgets")
            col.separator()
            col.row().prop(armature_id_store, "rigify_finalize_script", text="Run Script")
    
    
    class DATA_PT_rigify_samples(bpy.types.Panel):
    
        bl_label = "Samples"
    
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "data"
    
        bl_parent_id = "DATA_PT_rigify"
        bl_options = {'DEFAULT_CLOSED'}
    
    
        @classmethod
        def poll(cls, context):
            obj = context.object
            if not obj:
                return False
            return obj.type == 'ARMATURE' \
                and obj.data.get("rig_id") is None \
                and obj.mode == 'EDIT'
    
        def draw(self, context):
            layout = self.layout
            layout.use_property_split = True
            layout.use_property_decorate = False
            obj = context.object
            id_store = context.window_manager
    
            # Build types list
            build_type_list(context, id_store.rigify_types)
    
            if id_store.rigify_active_type > len(id_store.rigify_types):
                id_store.rigify_active_type = 0
    
            # Rig type list
    
            if len(feature_set_list.get_enabled_modules_names()) > 0:
    
                row.prop(context.object.data, "active_feature_set")
            row = layout.row()
            row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type')
    
            props = layout.operator("armature.metarig_sample_add", text="Add sample")
            props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name
    
    class DATA_PT_rigify_layer_names(bpy.types.Panel):
    
        bl_label = "Layer Names"
    
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "data"
        bl_options = {'DEFAULT_CLOSED'}
    
        bl_parent_id = "DATA_PT_rigify"
    
            if not context.object:
                return False
    
            return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
    
    
        def draw(self, context):
            layout = self.layout
            obj = context.object
    
    Campbell Barton's avatar
    Campbell Barton committed
            arm = obj.data
    
    Campbell Barton's avatar
    Campbell Barton committed
                for i in range(1 + len(arm.rigify_layers), 29):
                    arm.rigify_layers.add()
    
            else:
                # Can't add while drawing, just use button
    
                if len(arm.rigify_layers) < 29:
    
                    layout.operator("pose.rigify_layer_init")
                    return
    
            main_row = layout.row(align=True).split(factor=0.05)
    
            col1 = main_row.column()
            col2 = main_row.column()
            col1.label()
            for i in range(32):
                if i == 16 or i == 29:
                    col1.label()
    
    Campbell Barton's avatar
    Campbell Barton committed
            for i, rigify_layer in enumerate(arm.rigify_layers):
                # note: rigify_layer == arm.rigify_layers[i]
    
                    if i == 0:
                        col.label(text="Top Row:")
                    else:
                        col.label(text="Bottom Row:")
                if (i % 8) == 0:
    
                if i != 28:
                    row = col.row(align=True)
                    icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
                    row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
                    #row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
                    row.prop(rigify_layer, "name", text="")
                    row.prop(rigify_layer, "row", text="UI Row")
    
                    icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
                    row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
    
                    row.prop(rigify_layer, "group", text="Bone Group")
                else:
                    row = col.row(align=True)
    
                    icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
                    row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
                    # row.prop(arm, "layers", index=i, text="Layer %d" % (i + 1), toggle=True, icon=icon)
                    row1 = row.split(align=True).row(align=True)
                    row1.prop(rigify_layer, "name", text="")
                    row1.prop(rigify_layer, "row", text="UI Row")
                    row1.enabled = False
    
                    icon = 'RADIOBUT_ON' if rigify_layer.selset else 'RADIOBUT_OFF'
                    row.prop(rigify_layer, "selset", text="", toggle=True, icon=icon)
    
                    row.prop(rigify_layer, "group", text="Bone Group")
                if rigify_layer.group == 0:
                    row.label(text='None')
                else:
                    row.label(text=arm.rigify_colors[rigify_layer.group-1].name)
    
    
            col.label(text="Reserved:")
            # reserved_names = {28: 'Root', 29: 'DEF', 30: 'MCH', 31: 'ORG'}
            reserved_names = {29: 'DEF', 30: 'MCH', 31: 'ORG'}
            # for i in range(28, 32):
            for i in range(29, 32):
                row = col.row(align=True)
                icon = 'RESTRICT_VIEW_OFF' if arm.layers[i] else 'RESTRICT_VIEW_ON'
                row.prop(arm, "layers", index=i, text="", toggle=True, icon=icon)
                row.label(text=reserved_names[i])
    
    
    class DATA_OT_rigify_add_bone_groups(bpy.types.Operator):
        bl_idname = "armature.rigify_add_bone_groups"
        bl_label = "Rigify Add Standard Bone Groups"
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
            armature = obj.data
            if not hasattr(armature, 'rigify_colors'):
                return {'FINISHED'}
    
    
            groups = ['Root', 'IK', 'Special', 'Tweak', 'FK', 'Extra']
    
    
            for g in groups:
                if g in armature.rigify_colors.keys():
                    continue
    
                armature.rigify_colors.add()
                armature.rigify_colors[-1].name = g
    
    
                armature.rigify_colors[g].select = Color((0.3140000104904175, 0.7839999794960022, 1.0))
                armature.rigify_colors[g].active = Color((0.5490000247955322, 1.0, 1.0))
                armature.rigify_colors[g].standard_colors_lock = True
    
                if g == "Root":
                    armature.rigify_colors[g].normal = Color((0.43529415130615234, 0.18431372940540314, 0.41568630933761597))
                if g == "IK":
                    armature.rigify_colors[g].normal = Color((0.6039215922355652, 0.0, 0.0))
                if g== "Special":
                    armature.rigify_colors[g].normal = Color((0.9568628072738647, 0.7882353663444519, 0.0470588281750679))
                if g== "Tweak":
                    armature.rigify_colors[g].normal = Color((0.03921568766236305, 0.21176472306251526, 0.5803921818733215))
                if g== "FK":
                    armature.rigify_colors[g].normal = Color((0.11764706671237946, 0.5686274766921997, 0.03529411926865578))
                if g== "Extra":
                    armature.rigify_colors[g].normal = Color((0.9686275124549866, 0.250980406999588, 0.0941176563501358))
    
    
            return {'FINISHED'}
    
    
    class DATA_OT_rigify_use_standard_colors(bpy.types.Operator):
        bl_idname = "armature.rigify_use_standard_colors"
        bl_label = "Rigify Get active/select colors from current theme"
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
            armature = obj.data
            if not hasattr(armature, 'rigify_colors'):
                return {'FINISHED'}
    
    
            current_theme = bpy.context.preferences.themes.items()[0][0]
            theme = bpy.context.preferences.themes[current_theme]
    
    
            armature.rigify_selection_colors.select = theme.view_3d.bone_pose
            armature.rigify_selection_colors.active = theme.view_3d.bone_pose_active
    
            # for col in armature.rigify_colors:
            #     col.select = theme.view_3d.bone_pose
            #     col.active = theme.view_3d.bone_pose_active
    
            return {'FINISHED'}
    
    
    class DATA_OT_rigify_apply_selection_colors(bpy.types.Operator):
        bl_idname = "armature.rigify_apply_selection_colors"
        bl_label = "Rigify Apply user defined active/select colors"
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
            armature = obj.data
            if not hasattr(armature, 'rigify_colors'):
                return {'FINISHED'}
    
    
            #current_theme = bpy.context.preferences.themes.items()[0][0]
            #theme = bpy.context.preferences.themes[current_theme]
    
    
            for col in armature.rigify_colors:
                col.select = armature.rigify_selection_colors.select
                col.active = armature.rigify_selection_colors.active
    
            return {'FINISHED'}
    
    
    class DATA_OT_rigify_bone_group_add(bpy.types.Operator):
        bl_idname = "armature.rigify_bone_group_add"
        bl_label = "Rigify Add Bone Group color set"
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
            armature = obj.data
    
            if hasattr(armature, 'rigify_colors'):
                armature.rigify_colors.add()
                armature.rigify_colors[-1].name = unique_name(armature.rigify_colors, 'Group')
    
    
                current_theme = bpy.context.preferences.themes.items()[0][0]
                theme = bpy.context.preferences.themes[current_theme]
    
    
                armature.rigify_colors[-1].normal = theme.view_3d.wire
                armature.rigify_colors[-1].normal.hsv = theme.view_3d.wire.hsv
                armature.rigify_colors[-1].select = theme.view_3d.bone_pose
                armature.rigify_colors[-1].select.hsv = theme.view_3d.bone_pose.hsv
                armature.rigify_colors[-1].active = theme.view_3d.bone_pose_active
                armature.rigify_colors[-1].active.hsv = theme.view_3d.bone_pose_active.hsv
    
            return {'FINISHED'}
    
    
    class DATA_OT_rigify_bone_group_add_theme(bpy.types.Operator):
        bl_idname = "armature.rigify_bone_group_add_theme"
        bl_label = "Rigify Add Bone Group color set from Theme"
        bl_options = {"REGISTER", "UNDO"}
    
    
        theme: EnumProperty(items=(
            ('THEME01', 'THEME01', ''),
            ('THEME02', 'THEME02', ''),
            ('THEME03', 'THEME03', ''),
            ('THEME04', 'THEME04', ''),
            ('THEME05', 'THEME05', ''),
            ('THEME06', 'THEME06', ''),
            ('THEME07', 'THEME07', ''),
            ('THEME08', 'THEME08', ''),
            ('THEME09', 'THEME09', ''),
            ('THEME10', 'THEME10', ''),
            ('THEME11', 'THEME11', ''),
            ('THEME12', 'THEME12', ''),
            ('THEME13', 'THEME13', ''),
            ('THEME14', 'THEME14', ''),
            ('THEME15', 'THEME15', ''),
            ('THEME16', 'THEME16', ''),
            ('THEME17', 'THEME17', ''),
            ('THEME18', 'THEME18', ''),
            ('THEME19', 'THEME19', ''),
            ('THEME20', 'THEME20', '')
            ),
             name='Theme')
    
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
            armature = obj.data
    
            if hasattr(armature, 'rigify_colors'):
    
                if self.theme in armature.rigify_colors.keys():
                    return {'FINISHED'}
                armature.rigify_colors.add()
                armature.rigify_colors[-1].name = self.theme
    
                id = int(self.theme[-2:]) - 1
    
    
                theme_color_set = bpy.context.preferences.themes[0].bone_color_sets[id]
    
    
                armature.rigify_colors[-1].normal = theme_color_set.normal
                armature.rigify_colors[-1].select = theme_color_set.select
                armature.rigify_colors[-1].active = theme_color_set.active
    
            return {'FINISHED'}
    
    
    class DATA_OT_rigify_bone_group_remove(bpy.types.Operator):
        bl_idname = "armature.rigify_bone_group_remove"
        bl_label = "Rigify Remove Bone Group color set"
    
    
        idx: IntProperty()
    
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
            obj.data.rigify_colors.remove(self.idx)
    
            # set layers references to 0
            for l in obj.data.rigify_layers:
                if l.group == self.idx + 1:
                    l.group = 0
                elif l.group > self.idx + 1:
                    l.group -= 1
    
            return {'FINISHED'}
    
    
    class DATA_OT_rigify_bone_group_remove_all(bpy.types.Operator):
        bl_idname = "armature.rigify_bone_group_remove_all"
        bl_label = "Rigify Remove All Bone Groups"
    
        @classmethod
        def poll(cls, context):
    
            return context.object and context.object.type == 'ARMATURE'
    
    
        def execute(self, context):
            obj = context.object
    
            for i, col in enumerate(obj.data.rigify_colors):
                obj.data.rigify_colors.remove(0)
                # set layers references to 0
                for l in obj.data.rigify_layers:
                    if l.group == i + 1:
                        l.group = 0
    
            return {'FINISHED'}
    
    
    class DATA_UL_rigify_bone_groups(bpy.types.UIList):
        def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
            row = layout.row(align=True)
    
            row = row.split(factor=0.1)
    
            row.label(text=str(index+1))
    
            row = row.split(factor=0.7)
    
            row.prop(item, "name", text='', emboss=False)
            row = row.row(align=True)
            icon = 'LOCKED' if item.standard_colors_lock else 'UNLOCKED'
            #row.prop(item, "standard_colors_lock", text='', icon=icon)
            row.prop(item, "normal", text='')
            row2 = row.row(align=True)
            row2.prop(item, "select", text='')
            row2.prop(item, "active", text='')
            #row2.enabled = not item.standard_colors_lock
            row2.enabled = not bpy.context.object.data.rigify_colors_lock
    
    
    
    class DATA_MT_rigify_bone_groups_context_menu(bpy.types.Menu):
    
        bl_label = 'Rigify Bone Groups Specials'
    
        def draw(self, context):
            layout = self.layout
    
            layout.operator('armature.rigify_bone_group_remove_all')
    
    
    class DATA_PT_rigify_bone_groups(bpy.types.Panel):
    
        bl_label = "Bone Groups"
    
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "data"
        bl_options = {'DEFAULT_CLOSED'}
    
        bl_parent_id = "DATA_PT_rigify"
    
    
        @classmethod
        def poll(cls, context):
    
            if not context.object:
                return False
    
            return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None
    
    
        def draw(self, context):
            obj = context.object
            armature = obj.data
            color_sets = obj.data.rigify_colors
            idx = obj.data.rigify_colors_index
    
            layout = self.layout
            row = layout.row()
            row.operator("armature.rigify_use_standard_colors", icon='FILE_REFRESH', text='')
            row = row.row(align=True)
            row.prop(armature.rigify_selection_colors, 'select', text='')
            row.prop(armature.rigify_selection_colors, 'active', text='')
            row = layout.row(align=True)
            icon = 'LOCKED' if armature.rigify_colors_lock else 'UNLOCKED'
            row.prop(armature, 'rigify_colors_lock', text = 'Unified select/active colors', icon=icon)
            row.operator("armature.rigify_apply_selection_colors", icon='FILE_REFRESH', text='Apply')
            row = layout.row()
            row.template_list("DATA_UL_rigify_bone_groups", "", obj.data, "rigify_colors", obj.data, "rigify_colors_index")
    
            col = row.column(align=True)
    
            col.operator("armature.rigify_bone_group_add", icon='ADD', text="")
            col.operator("armature.rigify_bone_group_remove", icon='REMOVE', text="").idx = obj.data.rigify_colors_index
    
            col.menu("DATA_MT_rigify_bone_groups_context_menu", icon='DOWNARROW_HLT', text="")
    
            row = layout.row()
            row.prop(armature, 'rigify_theme_to_add', text = 'Theme')
            op = row.operator("armature.rigify_bone_group_add_theme", text="Add From Theme")
            op.theme = armature.rigify_theme_to_add
            row = layout.row()
            row.operator("armature.rigify_add_bone_groups", text="Add Standard")
    
    class BONE_PT_rigify_buttons(bpy.types.Panel):
        bl_label = "Rigify Type"
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "bone"
        #bl_options = {'DEFAULT_OPEN'}
    
        @classmethod
        def poll(cls, context):
    
            if not context.object:
                return False
    
            return context.object.type == 'ARMATURE' and context.active_pose_bone\
                   and context.active_object.data.get("rig_id") is None
    
    
        def draw(self, context):
            C = context
    
            bone = context.active_pose_bone
            rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "")
    
            layout = self.layout
    
            # Build types list
    
            build_type_list(context, id_store.rigify_types)
    
    
            # Rig type field
    
            if len(feature_set_list.get_enabled_modules_names()) > 0:
    
                row = layout.row()
                row.prop(context.object.data, "active_feature_set")
    
            row = layout.row()
    
            row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
    
    
            # Rig type parameters / Rig type non-exist alert
            if rig_name != "":
                try:
    
                    rig = rig_lists.rigs[rig_name]['module']
    
                except (ImportError, AttributeError, KeyError):
    
                    row = layout.row()
                    box = row.box()
    
                    box.label(text="ERROR: type \"%s\" does not exist!" % rig_name, icon='ERROR')
    
                    if hasattr(rig.Rig, 'parameters_ui'):
                        rig = rig.Rig
    
                        param_cb = rig.parameters_ui
    
                        # Ignore the known empty base method
                        if getattr(param_cb, '__func__', None) == base_rig.BaseRig.parameters_ui.__func__:
                            param_cb = None
    
                    except AttributeError:
    
                        col = layout.column()
                        col.label(text="No options")
    
                    else:
                        col = layout.column()
                        col.label(text="Options:")
                        box = layout.box()
    
                        param_cb(box, bone.rigify_parameters)
    
    class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
        bl_label = "Rigify Dev Tools"
        bl_space_type = 'VIEW_3D'
    
        bl_region_type = 'UI'
    
        bl_category = "Rigify"
    
        @classmethod
        def poll(cls, context):
            return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
    
    
        @classmethod
        def poll(cls, context):
            return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
    
    
        def draw(self, context):
    
            if obj is not None:
    
                if context.mode == 'EDIT_ARMATURE':
                    r = self.layout.row()
                    r.operator("armature.rigify_encode_metarig", text="Encode Metarig to Python")
                    r = self.layout.row()
                    r.operator("armature.rigify_encode_metarig_sample", text="Encode Sample to Python")
    
                if context.mode == 'EDIT_MESH':
                    r = self.layout.row()
                    r.operator("mesh.rigify_encode_mesh_widget", text="Encode Mesh Widget to Python")
    
    class VIEW3D_PT_rigify_animation_tools(bpy.types.Panel):
        bl_label = "Rigify Animation Tools"
        bl_context = "posemode"
        bl_space_type = 'VIEW_3D'
    
        bl_region_type = 'UI'
    
        bl_category = "Rigify"
    
            obj = context.active_object
            if obj and obj.type == 'ARMATURE':
                rig_id = obj.data.get("rig_id")
                if rig_id is not None:
                    has_arm = hasattr(bpy.types, 'POSE_OT_rigify_arm_ik2fk_' + rig_id)
                    has_leg = hasattr(bpy.types, 'POSE_OT_rigify_leg_ik2fk_' + rig_id)
                    return has_arm or has_leg
    
            return False
    
    
        def draw(self, context):
            obj = context.active_object
            id_store = context.window_manager
            if obj is not None:
                row = self.layout.row()
    
                if id_store.rigify_transfer_only_selected:
                    icon = 'OUTLINER_DATA_ARMATURE'
                else:
                    icon = 'ARMATURE_DATA'
    
                row.prop(id_store, 'rigify_transfer_only_selected', toggle=True, icon=icon)
    
                row = self.layout.row(align=True)
                row.operator("rigify.ik2fk", text='IK2FK Pose', icon='SNAP_ON')
                row.operator("rigify.fk2ik", text='FK2IK Pose', icon='SNAP_ON')
    
                row = self.layout.row(align=True)
                row.operator("rigify.transfer_fk_to_ik", text='IK2FK Action', icon='ACTION_TWEAK')
                row.operator("rigify.transfer_ik_to_fk", text='FK2IK Action', icon='ACTION_TWEAK')
    
                row = self.layout.row(align=True)
    
                row.operator("rigify.clear_animation", text="Clear IK Action", icon='CANCEL').anim_type = "IK"
                row.operator("rigify.clear_animation", text="Clear FK Action", icon='CANCEL').anim_type = "FK"
    
    
                row = self.layout.row(align=True)
                op = row.operator("rigify.rotation_pole", icon='FORCE_HARMONIC', text='Switch to pole')
                op.value = True
                op.toggle = False
                op.bake = True
                op = row.operator("rigify.rotation_pole", icon='FORCE_MAGNETIC', text='Switch to rotation')
                op.value = False
                op.toggle = False
                op.bake = True
    
                RIGIFY_OT_get_frame_range.draw_range_ui(context, self.layout)
    
    def rigify_report_exception(operator, exception):
        import traceback
        import sys
        import os
    
        # find the non-utils module name where the error happened
    
        # hint, this is the metarig type!
        exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
    
        fns = [ item.filename for item in traceback.extract_tb(exceptionTraceback) ]
        fns_rig = [ fn for fn in fns if os.path.basename(os.path.dirname(fn)) != 'utils' ]
        fn = fns_rig[-1]
    
        fn = os.path.basename(fn)
        fn = os.path.splitext(fn)[0]
        message = []
        if fn.startswith("__"):
            message.append("Incorrect armature...")
        else:
            message.append("Incorrect armature for type '%s'" % fn)
        message.append(exception.message)
    
        message.reverse()  # XXX - stupid! menu's are upside down!
    
    
        operator.report({'ERROR'}, '\n'.join(message))
    
    class LayerInit(bpy.types.Operator):
        """Initialize armature rigify layers"""
    
        bl_idname = "pose.rigify_layer_init"
        bl_label = "Add Rigify Layers"
    
    Campbell Barton's avatar
    Campbell Barton committed
            arm = obj.data
    
            for i in range(1 + len(arm.rigify_layers), 30):
    
    Campbell Barton's avatar
    Campbell Barton committed
                arm.rigify_layers.add()
    
            arm.rigify_layers[28].name = 'Root'
            arm.rigify_layers[28].row = 14
    
    def is_metarig(obj):
        if not (obj and obj.data and obj.type == 'ARMATURE'):
            return False
        if 'rig_id' in obj.data:
            return False
        for b in obj.pose.bones:
            if b.rigify_type != "":
                return True
        return False
    
    
    class Generate(bpy.types.Operator):
    
        """Generates a rig from the active metarig armature"""
    
    
        bl_idname = "pose.rigify_generate"
        bl_label = "Rigify Generate Rig"
    
        bl_description = 'Generates a rig from the active metarig armature'
    
        @classmethod
        def poll(cls, context):
            return is_metarig(context.object)
    
    
        def execute(self, context):
    
            metarig = context.object
    
                generate.generate_rig(context, metarig)
    
            except MetarigError as rig_exception:
    
                rigify_report_exception(self, rig_exception)
    
            except Exception as rig_exception:
                import traceback
                traceback.print_exc()
    
                self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception))
    
            else:
                self.report({'INFO'}, 'Successfully generated: "' + metarig.data.rigify_target_rig.name + '"')
    
            finally:
                bpy.ops.object.mode_set(mode='OBJECT')
    
    class UpgradeMetarigTypes(bpy.types.Operator):
        """Upgrades metarig bones rigify_types"""
    
        bl_idname = "pose.rigify_upgrade_types"
        bl_label = "Rigify Upgrade Metarig Types"
        bl_description = 'Upgrades the rigify types on the active metarig armature'
        bl_options = {'UNDO'}
    
        def execute(self, context):
            for obj in bpy.data.objects:
                if type(obj.data) == bpy.types.Armature:
                    upgradeMetarigTypes(obj)
            return {'FINISHED'}
    
    class Sample(bpy.types.Operator):
    
        """Create a sample metarig to be modified before generating the final rig"""
    
    
        bl_idname = "armature.metarig_sample_add"
    
        bl_label = "Add Metarig Sample"
        bl_options = {'UNDO'}
    
        metarig_type: StringProperty(
            name="Type",
            description="Name of the rig type to generate a sample of",
            maxlen=128,
    
        @classmethod
        def poll(cls, context):
            return context.mode == 'EDIT_ARMATURE'
    
        def draw(self, context):
            layout = self.layout
            layout.use_property_split = True
            layout.use_property_decorate = False
            col = layout.column()
            build_type_list(context, context.window_manager.rigify_types)
            col.prop(context.object.data, "active_feature_set")
            col.prop_search(self, "metarig_type", context.window_manager, "rigify_types")
    
        def invoke(self, context, event):
            if self.metarig_type == "":
                return context.window_manager.invoke_props_dialog(self)
            return self.execute(context)
    
    
        def execute(self, context):
    
            if self.metarig_type == "":
                self.report({'ERROR'}, "You must select a rig type to create a sample of.")
                return {'CANCELLED'}
            try:
                rig = rig_lists.rigs[self.metarig_type]["module"]
                create_sample = rig.create_sample
            except (ImportError, AttributeError, KeyError):
                raise Exception("rig type '" + self.metarig_type + "' has no sample.")
            else:
                create_sample(context.active_object)
            finally:
                bpy.ops.object.mode_set(mode='EDIT')
    
    class EncodeMetarig(bpy.types.Operator):
    
        """Creates Python code that will generate the selected metarig"""
    
        bl_idname = "armature.rigify_encode_metarig"
        bl_label = "Rigify Encode Metarig"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(self, context):
    
            return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
    
    
        def execute(self, context):
            name = "metarig.py"
    
            if name in bpy.data.texts:
                text_block = bpy.data.texts[name]
                text_block.clear()
            else:
                text_block = bpy.data.texts.new(name)
    
    
            text = write_metarig(context.active_object, layers=True, func_name="create", groups=True, widgets=True)
    
            text_block.write(text)
            bpy.ops.object.mode_set(mode='EDIT')
    
            self.report({'INFO'}, f"Metarig written to text datablock: {text_block.name}")
    
            return {'FINISHED'}
    
    
    class EncodeMetarigSample(bpy.types.Operator):
    
        """Creates Python code that will generate the selected metarig as a sample"""
    
        bl_idname = "armature.rigify_encode_metarig_sample"
        bl_label = "Rigify Encode Metarig Sample"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(self, context):
    
            return context.mode == 'EDIT_ARMATURE' and is_metarig(context.object)
    
    
        def execute(self, context):
            name = "metarig_sample.py"
    
            if name in bpy.data.texts:
                text_block = bpy.data.texts[name]
                text_block.clear()
            else:
                text_block = bpy.data.texts.new(name)
    
            text = write_metarig(context.active_object, layers=False, func_name="create_sample")
            text_block.write(text)
            bpy.ops.object.mode_set(mode='EDIT')
    
    
            self.report({'INFO'}, f"Metarig Sample written to text datablock: {text_block.name}")
    
    class VIEW3D_MT_rigify(bpy.types.Menu):
        bl_label = "Rigify"
        bl_idname = "VIEW3D_MT_rigify"
    
        def draw(self, context):
            layout = self.layout
    
            obj = context.object
    
            text = "Re-Generate Rig" if obj.data.rigify_target_rig else "Generate Rig"
            layout.operator(Generate.bl_idname, text=text)
    
            if context.mode == 'EDIT_ARMATURE':
                layout.separator()
                layout.operator(Sample.bl_idname)
                layout.separator()
                layout.operator(EncodeMetarig.bl_idname, text="Encode Metarig")
                layout.operator(EncodeMetarigSample.bl_idname, text="Encode Metarig Sample")
    
    
    def draw_rigify_menu(self, context):
        if is_metarig(context.object):
            self.layout.menu(VIEW3D_MT_rigify.bl_idname)
    
    
    class EncodeWidget(bpy.types.Operator):
        """ Creates Python code that will generate the selected metarig.
        """
        bl_idname = "mesh.rigify_encode_mesh_widget"
        bl_label = "Rigify Encode Widget"
        bl_options = {'UNDO'}
    
        @classmethod
        def poll(self, context):
            return context.mode == 'EDIT_MESH'
    
        def execute(self, context):
            name = "widget.py"
    
            if name in bpy.data.texts:
                text_block = bpy.data.texts[name]
                text_block.clear()
            else:
                text_block = bpy.data.texts.new(name)
    
            text = write_widget(context.active_object)
            text_block.write(text)
            bpy.ops.object.mode_set(mode='EDIT')
    
            return {'FINISHED'}
    
    
    def draw_mesh_edit_menu(self, context):
        self.layout.operator(EncodeWidget.bl_idname)
        self.layout.separator()
    
    
    def FktoIk(rig, window='ALL'):
    
        scn = bpy.context.scene
        id_store = bpy.context.window_manager
    
        rig_id = rig.data['rig_id']
        leg_ik2fk = eval('bpy.ops.pose.rigify_leg_ik2fk_' + rig_id)
        arm_ik2fk = eval('bpy.ops.pose.rigify_arm_ik2fk_' + rig_id)
        limb_generated_names = get_limb_generated_names(rig)
    
        if window == 'ALL':
    
            frames = get_keyed_frames_in_range(bpy.context, rig)
    
        elif window == 'CURRENT':
            frames = [scn.frame_current]
        else: