0
$\begingroup$

I'm trying to update this add-on to Blender 2.8, I've update some of the code, but I got stuck on some errors happening.

Traceback (most recent call last): File "/home/lucas/Desktop/Prêt-à-Template/01_3d/maternity_10.blend/ApplyModifierForObjectWithShapeKeys.py", line 107, in execute File "/home/lucas/Desktop/Prêt-à-Template/01_3d/maternity_10.blend/ApplyModifierForObjectWithShapeKeys.py", line 52, in applyModifierForObjectWithShapeKeys File "/home/lucas/Softwares/blender-2.81/2.81/scripts/modules/bpy/ops.py", line 201, in call ret = op_call(self.idname_py(), None, kw) TypeError: Converting py args to operator properties: : keyword "constraint_orientation" unrecognized

location: :-1

    bl_info = {
    "name":         "Apply modifier for object with shape keys",
    "author":       "Przemysław Bągard",
    "blender":      (2,80,0),
    "version":      (0,1,1),
    "location":     "Context menu",
    "description":  "Apply modifier and remove from the stack for object with shape keys (Pushing 'Apply' button in 'Object modifiers' tab result in an error 'Modifier cannot be applied to a mesh with shape keys').",
    "category":     "Object Tools > Multi Shape Keys"
}

import bpy, math
from bpy.props import *

# Algorithm:
# - Duplicate active object as many times as the number of shape keys
# - For each copy remove all shape keys except one
# - Removing last shape does not change geometry data of object
# - Apply modifier for each copy
# - Join objects as shapes and restore shape keys names
# - Delete all duplicated object except one
# - Delete old object
# - Restore name of object and object data

def applyModifierForObjectWithShapeKeys(context, modifierName):
    list_names = []
    list = []
    list_shapes = []
    if context.object.data.shape_keys:
        list_shapes = [o for o in context.object.data.shape_keys.key_blocks]

    if(list_shapes == []):
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)
        return context.view_layer.objects

    list.append(context.view_layer.objects)
    for i in range(1, len(list_shapes)):
        bpy.ops.object.duplicate_move(

         OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'},
         TRANSFORM_OT_translate={"value":(0, 0, 0),
         "constraint_axis":(False, False, False),
         "constraint_orientation":'GLOBAL',
         "mirror":False, "proportional":'DISABLED',
         "proportional_edit_falloff":'SMOOTH',
         "proportional_size":1,
         "snap":False,
         "snap_target":'CLOSEST',
         "snap_point":(0, 0, 0),
         "snap_align":False,
         "snap_normal":(0, 0, 0),
         "texture_space":False,
         "release_confirm":False})

        list.append(context.view_layer.objects)

    for i, o in enumerate(list):
        context.view_layer.objects = o
        list_names.append(o.data.shape_keys.key_blocks[i].name)
        for j in range(i+1, len(list))[::-1]:
            context.object_shape_key_index = j
            bpy.ops.object.shape_key_remove()
        for j in range(0, i):
            context.object_shape_key_index = 0
            bpy.ops.object.shape_key_remove()
        # last deleted shape doesn't change object shape
        context.object_shape_key_index = 0
        bpy.ops.object.shape_key_remove()
        # time to apply modifiers
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)

    bpy.ops.object.select_all(action='DESELECT')
    context.view_layer.objects = list[0]
    list[0].select = True
    bpy.ops.object.shape_key_add(from_mix=False)
    context.object.data.shape_keys.key_blocks[0].name = list_names[0]
    for i in range(1, len(list)):
        list[i].select = True
        bpy.ops.object.join_shapes()
        list[i].select = False
        context.object.data.shape_keys.key_blocks[i].name = list_names[i]

    bpy.ops.object.select_all(action='DESELECT')
    for o in list[1:]:
        o.select = True

    bpy.ops.object.delete(use_global=False)
    context.view_layer.objects = list[0]
    context.view_layer.objects.select = True
    return context.view_layer.objects

class ApplyModifierForObjectWithShapeKeysOperator(bpy.types.Operator):
    bl_idname = "object.apply_modifier_for_object_with_shape_keys"
    bl_label = "Apply Modifiers w/ Shape Keys"

    def item_list(self, context):
        return [(modifier.name, modifier.name, modifier.name) for modifier in bpy.context.view_layer.objects.active.modifiers]

    my_enum = EnumProperty(name="Modifier name",
        items = item_list)

    def execute(self, context):

        ob = context.view_layer.objects.active
        bpy.ops.object.select_all(action='DESELECT')
        ob.select_set(True)
        context.view_layer.objects.active = ob
        applyModifierForObjectWithShapeKeys(context, self.my_enum)

        return {'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)


class DialogPanel(bpy.types.Panel):
    bl_label = "Apply Modifiers w/ Shape Keys"
    bl_idname = "object.apply_modifier_for_object_with_shape_keys"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Edit"

    def draw(self, context):
        self.layout.operator("object.apply_modifier_for_object_with_shape_keys")


def menu_func(self, context):
    self.layout.operator("object.apply_modifier_for_object_with_shape_keys", 
        text="Apply Modifiers for Object with Shape Keys")

classes = ( ApplyModifierForObjectWithShapeKeysOperator, DialogPanel,)

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)

if __name__ == "__main__":
    register()  
$\endgroup$
1
$\begingroup$

I edited this a while ago for 2.7x and now for 2.83-alpha currently, this one preserves the values of the shapekeys and also hides other modifiers that could mess with the joining of shapekeys.

bl_info = {
    "name":         "Apply modifier for object with shape keys",
    "author":       "Przemysław Bągard",
    "blender":      (2,8,0),
    "version":      (0,1,0),
    "location":     "Context menu",
    "description":  "Apply modifier and remove from the stack for object with shape keys (Pushing 'Apply' button in 'Object modifiers' tab result in an error 'Modifier cannot be applied to a mesh with shape keys').",
    "category":     "Object Tools > Multi Shape Keys"
}

import bpy, math
from bpy.utils import register_class
from bpy.props import *

# Algorithm:
# - Duplicate active object as many times as the number of shape keys
# - For each copy remove all shape keys except one
# - Removing last shape does not change geometry data of object
# - Apply modifier for each copy
# - Join objects as shapes and restore shape keys names
# - Delete all duplicated object except one
# - Delete old object
# - Restore name of object and object data
def applyModifierForObjectWithShapeKeys(context, modifierName):

    list_properties = []
    properties = ["interpolation", "mute", "name", "relative_key", "slider_max", "slider_min", "value", "vertex_group"]
    list = []
    list_shapes = []
    list_modifiers = {}

    if context.view_layer.objects.active.data.shape_keys:
        list_shapes = [o for o in context.object.data.shape_keys.key_blocks]

    if(list_shapes == []):
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)
        return context.view_layer.objects.active
    #deactivate all modifiers in viewport
    for mod in context.view_layer.objects.active.modifiers:
        list_modifiers[mod.name] = mod.show_viewport
        mod.show_viewport = True if mod.name == modifierName else False

    list.append(context.view_layer.objects.active)
    for i in range(1, len(list_shapes)):
        bpy.ops.object.duplicate_move(
            OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'},
            TRANSFORM_OT_translate={"value":(0, 0, 0),
            "constraint_axis":(False, False, False),
            "orient_type":'GLOBAL', "mirror":False,
            "use_proportional_edit":False,
            "proportional_edit_falloff":'SMOOTH',
            "proportional_size":1,
            "snap":False,
            "snap_target":'CLOSEST',
            "snap_point":(0, 0, 0),
            "snap_align":False,
            "snap_normal":(0, 0, 0),
            "texture_space":False,
            "release_confirm":False})
        list.append(context.view_layer.objects.active)

    for i, o in enumerate(list):
        context.view_layer.objects.active = o
        key_b = o.data.shape_keys.key_blocks[i]
        print (o.data.shape_keys.key_blocks[i].name, key_b.name)
        properties_object = {p:None for p in properties}
        properties_object["name"] = key_b.name
        properties_object["mute"] = key_b.mute
        properties_object["interpolation"] = key_b.interpolation
        properties_object["relative_key"] = key_b.relative_key.name
        properties_object["slider_max"] = key_b.slider_max
        properties_object["slider_min"] = key_b.slider_min
        properties_object["value"] = key_b.value
        properties_object["vertex_group"] = key_b.vertex_group
        list_properties.append(properties_object)
        for j in range(i+1, len(list))[::-1]:
            context.object.active_shape_key_index = j
            bpy.ops.object.shape_key_remove()
        for j in range(0, i):
            context.object.active_shape_key_index = 0
            bpy.ops.object.shape_key_remove()
        # last deleted shape doesn't change object shape
        context.object.active_shape_key_index = 0
        bpy.ops.object.shape_key_remove()
        # time to apply modifiers
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)

    bpy.ops.object.select_all(action='DESELECT')
    context.view_layer.objects.active = list[0]
    context.view_layer.objects.active.select_set(True)
    bpy.ops.object.shape_key_add(from_mix=False)
    #workaround for "this type doesn't support IDProperties" error
    key_b0 = context.view_layer.objects.active.data.shape_keys.key_blocks[0]
    key_b0.name = list_properties[0]["name"]
    key_b0.interpolation = list_properties[0]["interpolation"]
    key_b0.mute = list_properties[0]["mute"]
    key_b0.slider_max = list_properties[0]["slider_max"]
    key_b0.slider_min = list_properties[0]["slider_min"]
    key_b0.value = list_properties[0]["value"]
    key_b0.vertex_group = list_properties[0]["vertex_group"]

    for i in range(1, len(list)):
        list[i].select_set(True)
        bpy.ops.object.join_shapes()
        list[i].select_set(False)
        key_b = context.view_layer.objects.active.data.shape_keys.key_blocks[i]
        key_b.name = list_properties[i]["name"]
        key_b.interpolation = list_properties[i]["interpolation"]
        key_b.mute = list_properties[i]["mute"]
        key_b.slider_max = list_properties[i]["slider_max"]
        key_b.slider_min = list_properties[i]["slider_min"]
        key_b.value = list_properties[i]["value"]
        key_b.vertex_group = list_properties[i]["vertex_group"]

    for i in range(0, len(list)):
        key_b = context.view_layer.objects.active.data.shape_keys.key_blocks[i]
        rel_key = list_properties[i]["relative_key"]

        for j in range(0, len(list)):
            key_brel = context.view_layer.objects.active.data.shape_keys.key_blocks[j]
            if rel_key == key_brel.name:
                key_b.relative_key = key_brel
                break

    bpy.ops.object.select_all(action='DESELECT')
    for o in list[1:]:
        o.select_set(True)

    bpy.ops.object.delete(use_global=False)
    context.view_layer.objects.active = list[0]
    context.view_layer.objects.active.select_set(True)
    #reset all modifiers in viewport
    for mod in context.view_layer.objects.active.modifiers:
        mod.show_viewport = list_modifiers[mod.name]
    return context.view_layer.objects.active

class ApplyModifierForObjectWithShapeKeysOperator(bpy.types.Operator):
    bl_idname = "object.apply_modifier_for_object_with_shape_keys"
    bl_label = "Apply modifier for object with shape keys"

    def item_list(self, context):
        return [(modifier.name, modifier.name, modifier.name) for modifier in bpy.context.view_layer.objects.active.modifiers]

    my_enum : EnumProperty(name="Modifier name",
        items = item_list)

    def execute(self, context):

        ob = context.view_layer.objects.active
        bpy.ops.object.select_all(action='DESELECT')
        context.view_layer.objects.active = ob
        ob.select_set(True)
        applyModifierForObjectWithShapeKeys(context, self.my_enum)

        return {'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)
register_class(ApplyModifierForObjectWithShapeKeysOperator)

class DialogPanel(bpy.types.Panel):
    bl_label = "Multi Shape Keys"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"

    def draw(self, context):
        self.layout.operator("object.apply_modifier_for_object_with_shape_keys")


def menu_func(self, context):
    self.layout.operator("object.apply_modifier_for_object_with_shape_keys", 
        text="Apply modifier for object with shape keys")

def register():
    register_class(DialogPanel)

def unregister():
    bpy.utils.unregister_module(__name__)
register()
|improve this answer|||||
$\endgroup$
  • $\begingroup$ Thank you Iszotic! :) $\endgroup$ – Lucas Falcão Feb 18 at 18:53
2
$\begingroup$

Re the error.

@Jeacom as kindly debugged and given you a working solution which is great, please take the time to compare working changes.

Instead I'll run thru what the error message listed in question is and how to fix.

If you are unsure what an error message means recommend using it as a search term on google or stackoverflow. eg "python keyword unrecognized"

From the error message

keyword "constraint_orientation" unrecognized

The error is pointing to this line

    bpy.ops.object.duplicate_move(

     OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'},
     TRANSFORM_OT_translate={"value":(0, 0, 0),
     "constraint_axis":(False, False, False),
     "constraint_orientation":'GLOBAL',

in the call to duplicate move operator. Which is in itself a macro of two operators object.duplicate and transform.translate.

The members of the dictionary TRANSFORM_OT_translate are the keyword arguments passed to the operator. The error is telling us constraint_orientation doesn't exist, it maybe mispelled.. or have been changed by devs

Ok let's look at bpy.ops.transform.translate() in the python console, auto complete key (defaults CtrlSpace or more recently the familiar Tab) pressed after the brace

>>> bpy.ops.transform.translate(
translate()
bpy.ops.transform.translate(value=(0, 0, 0), 
        orient_type='GLOBAL',
        orient_matrix=((0, 0, 0), (0, 0, 0), (0, 0, 0)), orient_matrix_type='GLOBAL', constraint_axis=(False, False, False), mirror=False, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), gpencil_strokes=False, cursor_transform=False, texture_space=False, remove_on_cancel=False, release_confirm=False, use_accurate=False)
Move selected items

and notice that there is no constraint_orientation keyword, there is however a orient_type keyword set to 'GLOBAL'. Do some tests or look up the documentation to clarify.. or simply by the sniff of it, replace the keyword and move onto success ... it works, or onto debugging the next error.

Worth mentioning any keyword that is being set to default can be left out, eg to call translate using all defaults but the value passed can simply use

bpy.ops.transform.translate(value=(0, 6, 0))

Also worth mentioning, if the value of the transform operator is zero could simply use

bpy.ops.object.duplicate() 

as done in other answer

|improve this answer|||||
$\endgroup$
  • $\begingroup$ Thank you for the explanation @batFINGER! $\endgroup$ – Lucas Falcão Dec 9 '19 at 19:42
1
$\begingroup$

I got it working.

bl_info = {
    "name":         "Apply modifier for object with shape keys",
    "author":       "Przemysław Bągard",
    "blender":      (2,80,0),
    "version":      (0,1,1),
    "location":     "Context menu",
    "description":  "Apply modifier and remove from the stack for object with shape keys (Pushing 'Apply' button in 'Object modifiers' tab result in an error 'Modifier cannot be applied to a mesh with shape keys').",
    "category":     "Object Tools > Multi Shape Keys"
}

import bpy, math
from bpy.props import *

# Algorithm:
# - Duplicate active object as many times as the number of shape keys
# - For each copy remove all shape keys except one
# - Removing last shape does not change geometry data of object
# - Apply modifier for each copy
# - Join objects as shapes and restore shape keys names
# - Delete all duplicated object except one
# - Delete old object
# - Restore name of object and object data

def applyModifierForObjectWithShapeKeys(context, modifierName):
    list_names = []
    list = []
    list_shapes = []
    if context.object.data.shape_keys:
        list_shapes = [o for o in context.object.data.shape_keys.key_blocks]

    if(list_shapes == []):
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)
        return context.view_layer.objects

    list.append(context.active_object)
    for i in range(1, len(list_shapes)):
        print(list)
        bpy.ops.object.duplicate()
        list.append(context.active_object)


    for i, o in enumerate(list):
        context.view_layer.objects.active = o
        list_names.append(o.data.shape_keys.key_blocks[i].name)
        for j in range(i+1, len(list))[::-1]:
            o.active_shape_key_index = j
            bpy.ops.object.shape_key_remove()
        for j in range(0, i):
            o.active_shape_key_index = 0
            bpy.ops.object.shape_key_remove()
        # last deleted shape doesn't change object shape
        o.active_shape_key_index = 0
        bpy.ops.object.shape_key_remove()
        # time to apply modifiers
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)

    bpy.ops.object.select_all(action='DESELECT')
    context.view_layer.objects.active = list[0]
    list[0].select_set(True)
    bpy.ops.object.shape_key_add(from_mix=False)
    context.object.data.shape_keys.key_blocks[0].name = list_names[0]
    for i in range(1, len(list)):
        list[i].select_set(True)
        bpy.ops.object.join_shapes()
        list[i].select_set(True)
        context.object.data.shape_keys.key_blocks[i].name = list_names[i]

    bpy.ops.object.select_all(action='DESELECT')
    for o in list[1:]:
        o.select_set(True)

    bpy.ops.object.delete(use_global=False)
    list[0].select_set(True)
    context.view_layer.objects.active = list[0]
    return context.view_layer.objects

class ApplyModifierForObjectWithShapeKeysOperator(bpy.types.Operator):
    bl_idname = "object.apply_modifier_for_object_with_shape_keys"
    bl_label = "Apply Modifiers w/ Shape Keys"

    def item_list(self, context):
        return [(modifier.name, modifier.name, modifier.name) for modifier in bpy.context.view_layer.objects.active.modifiers]

    my_enum = EnumProperty(name="Modifier name",
        items = item_list)

    def execute(self, context):

        ob = context.view_layer.objects.active
        bpy.ops.object.select_all(action='DESELECT')
        ob.select_set(True)
        context.view_layer.objects.active = ob
        applyModifierForObjectWithShapeKeys(context, self.my_enum)

        return {'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)


class DialogPanel(bpy.types.Panel):
    bl_label = "Apply Modifiers w/ Shape Keys"
    bl_idname = "object.apply_modifier_for_object_with_shape_keys"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Edit"

    def draw(self, context):
        self.layout.operator("object.apply_modifier_for_object_with_shape_keys")


def menu_func(self, context):
    self.layout.operator("object.apply_modifier_for_object_with_shape_keys", 
        text="Apply Modifiers for Object with Shape Keys")

classes = ( ApplyModifierForObjectWithShapeKeysOperator, DialogPanel,)

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)

if __name__ == "__main__":
    register()  
|improve this answer|||||
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.