Here I found a working solution using ctypes which allows to find out if any modal operator is currently running. I am not good in C and don't understand how it works. But I'm sure someone does, so is there a way to find out exactly which modal operators are currently running, and get a list of them (or their bl_idname)? As the Blender API only provides a few bpy.app.handlers which cover only several specific cases this would be a nice solution to make custom handlers for all other modal operators not included in the bpy.app.handlers.

For example for my add-on I would like to launch from my custom Modal Operator bpy.ops.render.opengl Operator in series in ('INVOKE_DEFAULT') mode to be able to see a preview and abort this process at any time - but there's no handler in the API which would tell me when bpy.ops.render.opengl is over, and I can't figure out how to catch the moment when it returns {"FINISHED"}.

  • $\begingroup$ By the way it seems the first Ctypes solution works only with Python Operators in Blender. I've tried to launch bpy.ops.render.opengl('INVOKE_DEFAULT'), which is written in C as I know, and use this solution together with threading.Timer() to catch the moment where all modal operators stop running. And it doesn't catch any modal operator while bpy.ops.render.opengl is in progress. Pity, it could be a workaround. $\endgroup$ Aug 11, 2020 at 18:23

1 Answer 1


You might be able to use something like the following to know when a modal operator has finished (see disclaimer). Note: I'm summarizing code from another project, and I haven't tested the code below... it almost certainly has bugs, but it will get you close.

class RenderOpenGLWrapped(Operator):
    bl_idname = 'render.opengl_wrapped'
    bl_label = 'Wraps render.opengl'
    # other options?

    def poll(cls, context):
        return bpy.ops.render.opengl.poll(context)

    def invoke(self, context, event):
        return {'RUNNING_MODAL'}

    def modal(self, context, event):
        last_ops = context.window.operators
        last_op_name = last_ops[-1].bl_idname if last_ops else None
        was_cancelled = (last_op_name != bpy.ops.render.opengl.idname())
        return {'FINISHED'}

this works because modal operators are pushed onto a stack. here the wrapper object is pushed on the stack, then render.opengl is pushed on. The window manager will call modal of the topmost operator, which is render.opengl. the wrapper's modal will not get called until render.opengl has finished.

disclaimer: this assumes modal for render.opengl doesn't return {'PASS_THROUGH'}!

If you're interested in knowing if there are any operators actively running modal, I just submitted the D15546 patch.


You must log in to answer this question.

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