2
$\begingroup$

I have a method that iterate through a collection(the assetsHandCollection), copies (link copy) the object to another collection(the gestureCollection) and then it should move the linked object. This is the method:

def make_gestures():
    gestureCollection = bpy.data.collections.get("Gesture")
    for hand in assetsHandCollection.objects:
        linkedCopyHand = hand.copy()
        gestureCollection.objects.link(linkedCopyHand)
        linkedCopyHand.hide_viewport = False
        linkedCopyHand.hide_render = False
        linkedCopyHand.hide_select = False
        bpy.context.view_layer.objects.active = linkedCopyHand
        bpy.ops.transform.translate(value=(50, 0, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, False, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)

The object are correctly linked to the new collection but they are not moved.

If I replace the line:

bpy.ops.transform.translate(value=(50, 0, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, False, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)

with

linkedCopyHand.matrix_world.translation += Vector((50, 0,0))

It works, the oject are correctly linked and translated.

If I check in the console which is the last active object after my script finished to run (with bpy.context.view_layer.objects.active), it is, correctly, one of the object that I have just link copied. If the console I try to run the command:

bpy.ops.transform.translate(value=(50, 0, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, False, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)

It return CANCELLED, without any further detail.

$\endgroup$
3
  • $\begingroup$ Would go with the "It works, the oject are correctly linked and translated." part and ditch the op. Why didn't it work:? My guess without testing is ob.select_set(True) to select as well as setting to active, since the op prob works only on selected objects. $\endgroup$
    – batFINGER
    Commented Apr 21, 2020 at 16:50
  • $\begingroup$ Or that the other collection being linked into doesn't have context. Anyway to reiterate wouldn't use the op. $\endgroup$
    – batFINGER
    Commented Apr 21, 2020 at 16:57
  • $\begingroup$ Thanks @batFINGER for your reply. Yes I know, If it works I should not care too much but still I would like to use the methods that the API provides and learn those, instead of patching the matrices directly. Just a question, what do you mean for "other collection being linked into doesn't have context" ? I see the source and destination collections in the outliner. $\endgroup$
    – edap
    Commented Apr 21, 2020 at 17:18

1 Answer 1

2
$\begingroup$

Objects need to be selected.

Since the operator works on all selected objects and you are translating each the same, could select and call the operator once. Tip only need to set non default values in an operator call

As an example will use default file, and set active object to None and assume no other objects selected

>>> C.view_layer.objects.active = None

Selecting works:

>>> for o in D.collections['Collection 1'].objects:
...     o.select_set(True)
...     
>>> bpy.ops.transform.translate(value=(0, 0, 1))
{'FINISHED'}

Overriding context

>>> c = {"selected_objects" : D.collections['Collection 1'].objects}
>>> bpy.ops.transform.translate(c, value=(0, 0, 1))
{'FINISHED'}

Putting it together to add a copy of each object in 'Collection 1' to 'foo'.

Copies the objects, links them to foo and moves them using one call of overridden operator.

>>> bpy.ops.object.select_all(action='DESELECT')
{'FINISHED'}

>>> foo = D.collections.new(name="Foo")
>>> C.scene.collection.children.link(foo)


>>> obs = [o.copy() for o in D.collections['Collection 1'].objects]
>>> for o in obs:
...     foo.objects.link(o)
...     

>>> bpy.ops.transform.translate({"selected_objects" : obs}, value=(0, 0, 1))
{'FINISHED'}

Ditch ops when possible

but still I would like to use the methods that the API provides and learn those, instead of patching the matrices directly.

IMO it's the other way around, the operator is basically a bridge between UI and API. For transforms you are far better off manipulating the matrices directly. There are some ops I would suggest you never use, eg adding a modifier. Why? see

https://blender.stackexchange.com/question/7358/python-performance-with-blender-operators

Hence you have already found what would have been my suggestion re answering this and manipulate the matrices directly and not rely on operators and a lot of diddling around to get context.

$\endgroup$
2
  • $\begingroup$ Thanks @batFINGER for your detailed answer. The most reliable way to move the object, with or without selection, it has been changing directly the matrix_world.translation, as you were suggesting. $\endgroup$
    – edap
    Commented Apr 23, 2020 at 20:53
  • $\begingroup$ Great answer, just wanted to let you know the link at the bottom is broken! $\endgroup$
    – SandPiper
    Commented May 4 at 0:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .