3
$\begingroup$

I have an Operator defined as ImportImageOperator which calls another operator TraceAlphaOperator but it does not trigger showing the Adjust Last Operation panel for the latter operator. But if I directly call TraceAlphaOperator then it shows the panel. But why not when it is invoked by another operator?

enter image description here

I tried calling bpy.ops.object.trace_alpha(param) from the ImportImageOperator and testing it with both parameters EXEC_DEFAULT and INVOKE_DEFAULT, but nothing works. Initially I had everything in one operator but that did not seem to work due to the import dialog that was in the way. So I decided to split the operation into those two aforementioned operators. This is the script:

import bpy

class TraceAlphaOperator(bpy.types.Operator):
    bl_idname = "object.trace_alpha"
    bl_label = "Trace alpha"
    bl_options = {'REGISTER', 'UNDO'} # 'INTERNAL'}

    threshold: bpy.props.FloatProperty(
        name="Threshold",
        default=1.0,
    )

    def execute(self, context):
        print("Execute TraceAlphaOperator")
        # do stuff like tracing the alpha
        return {'FINISHED'}

class ImportImageOperator(bpy.types.Operator):
    bl_idname = "object.import_image_and_trace"
    bl_label = "Import image and trace alpha"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        print("Execute ImportImageOperator")
        # do stuff like save loaded image and further processing ...
        result = bpy.ops.object.trace_alpha('INVOKE_DEFAULT')
        return result

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

def register():
    bpy.utils.register_class(ImportImageOperator)
    bpy.utils.register_class(TraceAlphaOperator)

def unregister():
    bpy.utils.unregister_class(ImportImageOperator)
    bpy.utils.unregister_class(TraceAlphaOperator)

if __name__ == "__main__":
    register()

The only workaround I know is to have two separate buttons for the two operators. First, I click one button to invoke ImportImageOperator to load the image, and then I click another button to invoke TraceAlphaOperator to perform the alpha tracing, which also displays the Adjust Last Operation panel. However, I would prefer pressing only one button to load and trace the alpha. Is this not possible?

$\endgroup$
4
  • 1
    $\begingroup$ It seems that the behavior to have adjustable parameters does not work if you have a dialog popup in between as tested when I removed the entire invoke function which then works. Can you not separate the operator that loads the image and then have another operator for the tracing? So your addon can have 1 button to load the image and then 1 button that will execute the tracing. That way you can show the Adjust Last Operation panel after you click the button to trace. $\endgroup$
    – Harry McKenzie
    Commented Jul 7 at 15:02
  • $\begingroup$ I did some simple test with one operator calling another operator and expecting the Adjust Last Operation panel to appear for the second operator but it's not working. So I'm interested in the answers to this question. Is it okay if I change your question's content to this simple scenario? $\endgroup$
    – Harry McKenzie
    Commented Jul 8 at 2:40
  • 1
    $\begingroup$ Yes, that's fine. $\endgroup$
    – stib
    Commented Jul 8 at 4:13
  • $\begingroup$ The whole thing is here: codeberg.org/stib/stibs_blender_scripts/src/branch/main/… $\endgroup$
    – stib
    Commented Jul 8 at 12:29

1 Answer 1

1
+100
$\begingroup$

Apparently the last adjust panel can't seem to be called from a script

import bpy

class SimpleOperator(bpy.types.Operator):
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_options = {"REGISTER", "UNDO", "BLOCKING"}

    test_float: bpy.props.FloatProperty()

    def execute(self, context):
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)

#def unregister():

if __name__ == "__main__":
    register()

    # test call
    a = next(e for e in bpy.context.screen.areas if e.type == "VIEW_3D")
    r = next(e for e in a.regions if e.type == "WINDOW")
    with bpy.context.temp_override(area=a, region=r):
        bpy.ops.object.simple_operator("INVOKE_DEFAULT")
        # bpy.ops.object.simple_operator("INVOKE_REGION_WIN")
        # bpy.ops.object.simple_operator("INVOKE_REGION_CHANNELS")
        # bpy.ops.object.simple_operator("INVOKE_REGION_PREVIEW")
        # bpy.ops.object.simple_operator("INVOKE_AREA")
        # bpy.ops.object.simple_operator("INVOKE_SCREEN")
        # bpy.ops.object.simple_operator("EXEC_DEFAULT")
        # bpy.ops.object.simple_operator("EXEC_REGION_WIN")
        # bpy.ops.object.simple_operator("EXEC_REGION_CHANNELS")
        # bpy.ops.object.simple_operator("EXEC_REGION_PREVIEW")
        # bpy.ops.object.simple_operator("EXEC_AREA")
        # bpy.ops.object.simple_operator("EXEC_SCREEN")
        # I tested 12 of them already

But you can simulate that situation through keyboard shortcut

import bpy
from bpy.types import PointerProperty

class ShareProps(bpy.types.Operator):
    test_float: bpy.props.FloatProperty()

class SimpleOperator(ShareProps):
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_options = {"REGISTER", "UNDO", "BLOCKING"}

    def execute(self, context):
        print(self.test_float)
        return {'FINISHED'}

class Main(ShareProps):
    bl_idname = "object.main"
    bl_label = "Main Operator"
    bl_options = {"REGISTER", "UNDO", "BLOCKING"}

    def __init__(self):
        kwargs = {}
        self.kwargs = kwargs

        for identifier, e in self.properties.bl_rna.properties.items():
            if isinstance(e, PointerProperty):
                continue
            
            kwargs[identifier] = getattr(self, identifier)

    def execute(self, context):
        kwargs = self.kwargs
        for k in kwargs:
            kwargs[k] = getattr(self, k)

        bpy.ops.object.simple_operator(**kwargs)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.utils.register_class(Main)

#def unregister():

if __name__ == "__main__":
    register()

Maybe little help, as it won't solve the core problem.

$\endgroup$
1
  • $\begingroup$ Thank you for sharing! :) $\endgroup$
    – Harry McKenzie
    Commented Aug 23 at 11:35

You must log in to answer this question.

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