Page 1 of 1

How to programmatically call operators in Python

Posted: Mon Jul 22, 2013 2:19 am
by dvochin
Hi, I am following very closely the tutorial at ... m_Property on how to create a panel and operations that I can invoke through the tool shelf GUI (to left of 3dView).

The provided tutorial works great if I click the 3dView button to make a tetrahedra, but if I attempt to run the same operator that the GUI button gives me (bpy.ops.mesh.make_tetrahedron()) in the Python command window, it spits out "{PASS_THROUGH}" and I get an error in the log saying "wm_operator_invoke: invalid operator call 'MESH_OT_make_tetrahedron'"

Presumably the operator needs to be called with the 3D View having the focus, but this is not possible in my case.

How can I call bpy.ops.mesh.make_tetrahedron() and have it work from within Python code??

Many thanks!


Posted: Mon Jul 22, 2013 11:23 am
by CoDEmanX
some notes on the code:

One should use
- no whitespace between parathesis of class inheritance and the colon
- no CamelCase for variables (should only be used for class names) such as "TheCol", better comply to stock scripts and use "col"
- proper operator naming (primitive_tetrahedron_add instead of make_tetrahedron)
- bl_options = {'REGISTER', 'UNDO'} for such an operator, I see no reason for omitting the 'REGISTER'
- execute() for the actual operator code, not invoke() - this is what makes it PASS_THROUGH if called from commandline, since 'EXECUTE_DEFAULT' is the default here, in viewport it is 'INVOKE_DEFAULT'
- bpy.utils.register_module(__name__) over several register_class() calls IMO, so there's no chance you forget a class
- operator properties if needed. I would remove the "Update Down" checkbox from the panel and instead leave it up for the redo panel, but I left it it and forward the state to the operator
- from xxx import yyy here, so you can i.e. ommit "mathutils" in "mathutils.Vector"
- code that works similar to similar operators from user perspective, it should IMO clear current selection and make the new object selected/active
- a poll() function to prevent operator execution if it's not available (in this case, the operator has no code to add geometry to an existing mesh object if currently in editmode - like the stock operators do)
- scene.update() somewhere after
- PEP8 says there should be spaces around python operators (e.g. "="), but not inside a function call!

Here's the improved version, that can be called via command line:

Code: Select all

import bpy
from math import sqrt
from mathutils import Vector
class TetrahedronMakerPanel(bpy.types.Panel):
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_context = "objectmode"
    bl_label = "Add Tetrahedron"
    def draw(self, context):
        col = self.layout.column(align=True)
        col.prop(context.scene, "make_tetrahedron_inverted")
        props = col.operator("mesh.make_tetrahedron", text="Add Tetrahedron")
        props.inverted = context.scene.make_tetrahedron_inverted
    #end draw
#end TetrahedronMakerPanel
class MakeTetrahedron(bpy.types.Operator):
    bl_idname = "mesh.primitive_tetrahedron_add"
    bl_label = "Add Tetrahedron_"
    bl_options = {'REGISTER', 'UNDO'}
    inverted = bpy.props.BoolProperty(
        name="Upside Down",
        description="Generate the tetrahedron upside down",
    def poll(cls, context):
        return context.mode == 'OBJECT'

    def execute(self, context):
        scale = -1 if self.inverted else 1
        vertices = \
            Vector((0, -1 / sqrt(3),0)),
            Vector((0.5, 1 / (2 * sqrt(3)), 0)),
            Vector((-0.5, 1 / (2 * sqrt(3)), 0)),
            Vector((0, 0, scale * sqrt(2 / 3))),
        me ="Tetrahedron")
        me.from_pydata \
            [[0, 1, 2], [0, 1, 3], [1, 2, 3], [2, 0, 3]]
        ob ="Tetrahedron", me)
        ob.location = context.scene.cursor_location
        bpy.ops.object.select_all(action='DESELECT') = True = ob
        return {"FINISHED"}
    #end invoke
#end MakeTetrahedron
def register() :
    bpy.types.Scene.make_tetrahedron_inverted = bpy.props.BoolProperty \
        name = "Upside Down",
        description = "Generate the tetrahedron upside down",
        default = False
#end register
def unregister() :
    del bpy.types.Scene.make_tetrahedron_inverted
#end unregister
if __name__ == "__main__" :
#end if

Posted: Mon Jul 22, 2013 7:32 pm
by dvochin
Hi Codemanx, many thanks for taking the time with these important corrections.

It works! I'm now able to call the operator from Python and a tetrahedron is created!

Unfortunately for me however, my attempt to call a blender function that requires to be called from the context of the 3dView from python is not working. (As it's a different topic I've rephrased my questions at this thread ... 749#105749)

Many thanks!

P.S. The code complains about the 'inverted' property when you run it...

Posted: Tue Jul 23, 2013 3:12 am
by CoDEmanX
you might wanna look into context overriding: ... ng-context

also check out the API docs about poll fail

Posted: Tue Jul 23, 2013 4:33 am
by dvochin
Hi CodeManx, I'm trying with context overriding and changing execution context but after much reading resources like ... -incorrect don't seam to point to any answer.

Doing the exercise of running the following code is yielding some insight, but the problem I'm having is how to obtain from within a python script the proper objects to stuff into the context!

Code: Select all

def execute(self,context):
    return {'FINISHED'}
In other words, the following code seem to have the right approach but getting the proper objects to stuff into is like finding a needle in a haystack...

Code: Select all

import bpy
for window in
    screen = window.screen
    for area in screen.areas:
        if area.type == 'VIEW_3D':
            override = {'window': window, 'screen': screen, 'area': area}
Am I on the right track by calling bpy.ops.mesh.knife_project() with my override and stuffing the right data in there?

The question now is how to stuff in the right data... Am poring through .py code that comes with blender for strings like INVOKE_ and EXEC_ to see what they do...

Any hint?


Posted: Wed Jul 24, 2013 1:59 am
by dvochin
For those following this I found a solution and I posted it at ... 783#105783


Posted: Wed Jul 24, 2013 2:24 am
by CoDEmanX
what context member an operator requires is hard to tell, to be sure what it actually checks and uses, you need to dive into the C-code.

But you can also try to call an operator with an empty context and see the system console for PyContext messages, like


it should list some of the required members. If you provide them, it might complain about others missing (and didn't tell you before). Beware of scene bases, they are often needed and if missing, blender might crash - and it rarely tells you about that they are required.

Posted: Wed Jul 24, 2013 2:33 am
by dvochin
Yup... agreed there... had I not been tracing through blender's code for bpy.ops.mesh.knife_project() and its complex contexts checks it would have taken longer!

But you're right, the very helpful 'PyContext: 'xyz' not found log at least told me what was missing and it was a breeze to fix.

I recommend posting the code somewhere in blender's docs near the section about operator context override... tough nut for noobs to crack and documentation sure could use a code snippet...