8
$\begingroup$

Is there a better way to copy a modifier with all attributes from active object to selected object through python scripting other than adding a modifier to selected and copy attributes one by one (with dictionary for example) from active?

The problem is some attributes are read-only (even if it makes no sense - like for example subdivision algorithm in Subsurf modifier) and i don't know how to set them through python script. So i can't copy that subdivision algorithm value from one to another through scripting with method setting a value for an attribute:

bpy.data.objects[object_name].modifiers[modifier_name].attribute_name = attribute_value

So to reiterate my question: is there any other way to add a modifier to an object that would allow setting up values for all attributes?

$\endgroup$
  • $\begingroup$ You could copy the object (incl. modifiers) and let a script set all non-modifier related properties to the target object's properties, but it's basically the same as copying modifier attributes... $\endgroup$ – CoDEmanX Nov 15 '13 at 1:38
  • $\begingroup$ You may want to investigate the source of the Copy Attributes add-on. It provides the functionality of copying modifiers. At the very least, you can see how it's done there. $\endgroup$ – Fweeb Nov 15 '13 at 18:38
14
$\begingroup$

I don't think that's possible.

A modifier can only be inserted through modifiers.new, so its properties do need to be copied individually. The problem here is in checking which properties can be copied. (e.g., Subsurf modifier's subdivision_type property is actually not readonly as you claimed, but we'd need to test it manually or check the API reference to find out.)

The whole "check writability then copy" procedure can be done more efficiently, though. For example, see this function:

import bpy

def main():
    active_object = bpy.context.object
    selected_objects = [o for o in bpy.context.selected_objects
                        if o != active_object and o.type == active_object.type]

    for obj in selected_objects:
        for mSrc in active_object.modifiers:
            mDst = obj.modifiers.get(mSrc.name, None)
            if not mDst:
                mDst = obj.modifiers.new(mSrc.name, mSrc.type)

            # collect names of writable properties
            properties = [p.identifier for p in mSrc.bl_rna.properties
                          if not p.is_readonly]

            # copy those properties
            for prop in properties:
                setattr(mDst, prop, getattr(mSrc, prop))

main()

It copies all modifiers from active object, to every other selected objects of similar type. What modifier properties to copy is determined by checking is_readonly attribute of all items in the modifier's bl_rna.properties. This way, we can ensure that only writable properties get copied.

In the API Reference, some readonly attributes like Dynamic Paint's brush_settings have no other method of access, so they can be set only through the UI.

$\endgroup$
  • $\begingroup$ Thanks for clarifying. For some reason i still can't write some attributes with my code (i've simply used try/except to avoid read-only attributes, so there must be other reason that prevented some of the attributes from updating), but your code works without a problem. Thank you. $\endgroup$ – kilbee Nov 15 '13 at 12:24
  • $\begingroup$ Truly awesome answer! $\endgroup$ – Antoni4040 Jun 10 '14 at 12:01
2
$\begingroup$

A simpler method than Adhi's is to use make_links_data

Say you make two cubes. You apply modifiers to the first one and want to copy them to the second one. Then your code should look something like this:

import bpy

mod_obj = bpy.context.scene.objects["Cube"]           # cube with mods
modable_obj = bpy.context.scene.objects["Cube.001"]   # cube without mods

mod_obj.select = True
bpy.context.scene.objects.active = mod_obj
modable_obj.select = True

bpy.ops.object.make_links_data(type='MODIFIERS')

bpy.ops.object.select_all(action='DESELECT')
$\endgroup$
1
$\begingroup$

Based on Adhi's answer.

Maybe below can be useful for someone in the future. Can be more Pythonic and beautiful, but I think below should collect all the Modifier Props from ob for obtarget, providing obtarget already has all the Modifiers being generated based on ob.

import bpy

ob = bpy.context.active_object
obtarget = bpy.data.objects['Cube.001']

mod_props = []

for mod in ob.modifiers:
    properties = []
    for prop in mod.bl_rna.properties:
        if not prop.is_readonly:
            properties.append(prop.identifier)
    mod_props.append([mod, properties])

for stuff in mod_props:
    for prop in stuff[1]:
        setattr(obtarget.modifiers[stuff[0].name], prop, getattr(stuff[0], prop))
$\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.