I am currently writing a python script and call a modal operator of an external addon like this: bpy.ops.uvpackmaster2.uv_pack("INVOKE_DEFAULT")

My problem now is, that this call only returns {'RUNNING_MODAL'} but I need to wait till this is finished before I run other tasks. Furthermore this operator can also fail - or let's not say fail, it can require further action and a further run.

I am looking for something like the 'addEventListerner' known from JavaScript or anything like that. I am very known to programming but kind of new to python. Is there something similar I can use?

Also, the second part of the question: I use the addon UVPackmaster2. If I invoke the uv_pack function the addon at first checks if there are some problems with the uv_map, if so the problematic faces get selected. If there are no problems the packing process starts and ends after a few seconds/minutes. It looks like there is no specific output I can use to determine if the process requires further investigation or if it run properly. I only thing I saw, was that the plugin puts output on the report via self.report(...). But I really don't like the idea to parse the report output to get the information in which state the process is. Do you have any better ideas? Or how I could patch the addon to support me with useful output? But I don't know how a modal operator should communicate with my script.

I allready found this Running code after a modal operator finished its job but I really don't understand how the example works. And how I could adapt this to my problem.

thanks and best regards!


1 Answer 1


After a week of reasearch and discussion on blenderartist iceythe helped me finding a solution writing a wrapper for the uv packmaster modal of the pack operator. (bpy.ops.uvpackmaster2.uv_pack())


import bpy
from uvpackmaster2 import UVP2_OT_PackOperator as op
from .bake_operator import callback

def modal_wrap(modal_func):

    def wrap(self, context, event):
        ret, = retset = modal_func(self, context, event)
        if ret in {'FINISHED'}:
            print(f"{self.bl_idname} returned {ret}")
        return retset
    return wrap

op._modal_org = op.modal
op.modal = modal_wrap(op.modal)

And an operator I tried it with :

import bpy

retval = None

def callback(ret):  # <- Modal wrapper calls this.
    global retval
    retval = ret
    bpy.ops.object.bake('INVOKE_DEFAULT', type="AO")

class BakteTexureOperator(bpy.types.Operator):
    bl_idname = "object.bake_texture"
    bl_label = "Bake Texure"

    def invoke(self, context, event):

            print("after invoke")
            wm = context.window_manager
            return {'RUNNING_MODAL'}

    def modal(self, context, event):
            if event.type in {'RIGHTMOUSE', 'ESC'}:
                return {'CANCELLED'}

            elif retval is not None:
                return self.execute(context)

            return {'PASS_THROUGH'}

    def execute(self, context):
            global retval
            val = retval
            retval = None  # Reset.

            if 'CANCELLED' in val:
                print("operator was cancelled")
                return {'CANCELLED'}

                print("operator finished")
                return {'FINISHED'}

To try it out just install the addon, select an image you want to bake to in the node editor and click the bake button in the bake testing panel.

It will unrwap and pack the uv's and the best thing is that you get a progress bar and you can cancel the baking. It's all comming from blender by running the bake operator as a modal.

The code for the full test bake addon can be found here :

Bake Testing

  • $\begingroup$ I wish I was able to extract a generic solution from this for all other cases where things need to be waited to finish for. My python skills are not enough for that, I guess :( $\endgroup$
    – n1cK
    Feb 4, 2021 at 19:18
  • $\begingroup$ I have a similar issue with a call to an op of another addon. I am trying to understand how to use a similar approach but I am not sure I get it correctly. In my case I have the following code snippet: obs = [o for o in bpy.data.objects if o.type == 'MESH'] for ob in obs: bpy.ops.object.select_all(action='DESELECT') # Deselect all objects bpy.context.view_layer.objects.active = ob if ob: ob.select_set(True) print(ob.name) ret = bpy.ops.qremesher.remesh() print('Ret: {}'.format(ret)) but my loop is not waiting the remesher op to finish. $\endgroup$
    – ttsesm
    Apr 9, 2021 at 0:51
  • $\begingroup$ Any idea whether the above solution would work for my case? $\endgroup$
    – ttsesm
    Apr 9, 2021 at 0:53
  • $\begingroup$ @lorenz-wieseke any idea how to use this solution with my script? I have my open thread here blender.stackexchange.com/q/218620/106722 if you wanna check on the updated section. Thanks. $\endgroup$
    – ttsesm
    Apr 16, 2021 at 10:49

You must log in to answer this question.

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