Operators¶
A special class of important API routines are the so-called operators. These are usually higher-level operations, such as adding a new cube mesh, deleting the current set of selected objects or running a file importer. As noted earlier many parts of the Blender UI are set up with Python scripts and in a lot of cases the operations you perform in the UI through menu actions or shortcut keys will simply call the relevant operator from Python to do the actual work.
The Info area will show most operators as they get executed, but you can also check what API call is made for a certain UI element (this requires Python Tooltips to be enabled, see developer settings). For example, adding a plane mesh through the Add menu will call the operator bpy.ops.mesh.primitive_plane_add()
, as the tooltip shows:
You can simply call the operator directly from Python to add a plane in exactly the same way as with the menu option:
>>> bpy.data.objects.values()
[]
>>> bpy.ops.mesh.primitive_plane_add()
{'FINISHED'}
# A plane mesh has now been added to the scene
>>> bpy.data.objects.values()
[bpy.data.objects['Plane']]
Many of the operators take parameters, to influence the results. For example, with bpy.ops.mesh.primitive_plane_add()
you can set the initial size and location of the plane (see the API docs for all the parameters):
Info
Note that operator parameters can only be passed using keyword arguments.
Operator context¶
Operators are very nice and powerful, but they have a few inherent properties that can make them tricky to work with.
An operator's execution crucially depends on the context in which it is called, where it gets most of the data it needs. As shown above simple parameter values can usually be passed, but values like the object(s) to operate on are retrieved implicitly. For example, to join a set of mesh objects into a single mesh you can call the operator bpy.ops.object.join()
. But the current context needs to be correctly set for the operator to work:
# We have no objects selected
>>> bpy.context.selected_objects
[]
>>> bpy.ops.object.join()
Warning: Active object is not a selected mesh
{'CANCELLED'}
# With 3 objects selected
>>> bpy.context.selected_objects
[bpy.data.objects['Cube'], bpy.data.objects['Cube.001'],
bpy.data.objects['Cube.002']]
# Now it works
>>> bpy.ops.object.join()
{'FINISHED'}
As can be seen above an operator call only returns a value indicating the execution status. When calling the operator in the Python Console as above some extra info is printed. But when calling operators from scripts the status return value is all you have to go on, as the extra message isn't printed when the script is executed. And in some cases the reason an operator fails can be quite unclear:
>>> bpy.context.selected_objects
[bpy.data.objects['Cube'], bpy.data.objects['Cube.001']]
>>> bpy.ops.mesh.intersect_boolean()
Traceback (most recent call last):
File "<blender_console>", line 1, in <module>
File "/usr/share/blender/4.1/scripts/modules/bpy/ops.py", line 109, in __call__
ret = _op_call(self.idname_py(), None, kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Operator bpy.ops.mesh.intersect_boolean.poll() failed, context is incorrect
This shows that the so-called poll()
function failed, but what does that mean? The poll function is used by operators to determine if they can execute in the current context. They do this by checking certain preconditions on things like the selected object(s), the type of data or an object's mode. In this case the bpy.ops.mesh.intersect_boolean()
operator can't perform a boolean intersection on multiple meshes, but only on the faces of a single object in edit mode, but this is not something you can tell from the error message (nor does the documentation make that clear):
One way to actually perform a boolean intersection on two objects from a Python script is to do what we would do in the UI: add a Boolean modifier on one of the objects and set its parameters, including a reference to the second object. We can take advantage of the Python Tooltips to see which operator we need:
This would suggest that using bpy.ops.object.modifier_add(type='BOOLEAN')
would be what we need, but then setting the required parameters on the modifier (i.e. the object to subtract) would become tricky. So specifically for doing a boolean operation using a Boolean modifier is a better option, as shown in an earlier chapter.
Unfortunately, certain operations can only be performed by calling operators. So there's a good chance that you will need to use them at some point when doing Python scripting. Hopefully this section gives some clues as how to work with them.
See this section for more details on all the above subtleties and issues relating to working with operators. The bpy.ops
documentation also contains useful information on operators, including how to override an operator's implicit context with values you set yourself.