0
$\begingroup$

I apologize for the volume of questions. I tried searching for this, but came up with nothing related.

When building an operator window (or panel for that matter), is there any way to setup a simple button -> function relationship? Say, for example, if I had 6 buttons that I wanted to have call 6 separate functions?

If this is not possible, would there be a clever way to create a single operator class that allows its caller to specify a custom function to call when it is executed? Something like this?

op = layout.operator( "wm.function_caller", text="Write Data")
op.function = self.WriteData

I hadn't considered this until writing this question, so I think I will go try some variations on this. If it works, the down-side would be the inability to specify a custom description tool-tip for the buttons, right?

Thanks - any advice is appreciated.

Edit: Just wanted to provide a simple example of where something like this might make sense. If you define an operator window that allows the user to manipulate multiple sets of the same data, you may provide buttons at the top of it to "add" a set, to "subtract" a set, to "move forward" a set in order, to "move back" a set in order, etc. I find it strange that Blender provides a simple callback mechanism for data changes, but doesn't provide one for buttons.

$\endgroup$
  • $\begingroup$ Why don't you create 6 operator classes each with their specific function? I'm not entirely sure what problem you're trying to solve. $\endgroup$ – Robert Gützkow Oct 13 '19 at 14:03
  • $\begingroup$ Condensed this into one comment: For one, I wasn't sure if it was possible to execute operators from within another operator. Since my buttons exist in an operator window, it seemed reasonable to map its buttons to its functions. But my primary reason for wanting to call functions was because the buttons were supposed to execute actions for the window operator and use/manipulate its data/properties. Some of that data cannot exist as properties, so I wasn't sure how to transmit the data back and forth between operators. $\endgroup$ – Robert Oct 13 '19 at 14:42
1
$\begingroup$

The following example shows how to create multiple operators each with their specific function. It only shows three buttons, but the concept remains the same for an arbitrary number of buttons and their underlying operators. The panel is created in the sidebar of the 3D View which can be opened with N.

I hope I haven't misunderstood what you're trying to accomplish. In case I did, feel free to leave a comment.

bl_info = {
    "name": "Multiple operator example",
    "author": "Robert Guetzkow",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > Sidebar > My own addon",
    "description": "Example with multiple operators",
    "warning": "",
    "wiki_url": "",
    "category": "3D View"}

import bpy


class EXAMPLE_OT_func_1(bpy.types.Operator):
    bl_idname = "example.func_1"
    bl_label = "Function 1"

    def execute(self, context):
        # Implement your first function here
        self.report({'INFO'}, f"This is {self.bl_idname}")
        return {'FINISHED'}


class EXAMPLE_OT_func_2(bpy.types.Operator):
    bl_idname = "example.func_2"
    bl_label = "Function 2"

    def execute(self, context):
        # Implement your second function here
        self.report({'INFO'}, f"This is {self.bl_idname}")
        return {'FINISHED'}


class EXAMPLE_OT_func_3(bpy.types.Operator):
    bl_idname = "example.func_3"
    bl_label = "Function 3"

    def execute(self, context):
        # Implement your third function here
        self.report({'INFO'}, f"This is {self.bl_idname}")
        return {'FINISHED'}


class EXAMPLE_PT_panel(bpy.types.Panel):
    bl_label = "My own addon"
    bl_category = "Name of your tab"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        layout.operator(EXAMPLE_OT_func_1.bl_idname)
        layout.operator(EXAMPLE_OT_func_2.bl_idname)
        layout.operator(EXAMPLE_OT_func_3.bl_idname)


classes = (EXAMPLE_OT_func_1, EXAMPLE_OT_func_2, EXAMPLE_OT_func_3, EXAMPLE_PT_panel)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()
$\endgroup$
  • $\begingroup$ Thanks man, I appreciate the detailed example. I was thinking of situations where you would have a lot of simple actions associated with a single operator. Your example highlights the fact that defining an operator is just as simple as defining a function. In my situation, I need the secondary operators to some how communicate with my primary operator. This is why I wanted to call its functions with the buttons. But there are probably other ways to do it. $\endgroup$ – Robert Oct 13 '19 at 14:35
  • 1
    $\begingroup$ In general you would use some class that stores that for that purpose. Since you've added a clarification after I've written my answer, it could be that you're looking for a way to implement a UIList similar to how the UI for the material slots works with the add, subtract, move up and down? $\endgroup$ – Robert Gützkow Oct 13 '19 at 18:29
  • $\begingroup$ In my specific case, it's a list of override pages, where the user can add, remove, and switch between pages (left, right). It's not complex. I just hoped I could keep all of the functionality inside the single operator. Have the buttons call event functions that change/add/remove pages. I'm still trying to plan it out. Thanks a bunch for the advice. $\endgroup$ – Robert Oct 14 '19 at 11:47
1
$\begingroup$

You can use a property, which is set differently for each function in the panel.

  1. Add a property to your operator.
    custom_prop = bpy.props.StringProperty(default="Default Value.")
  2. Add the operator to your UI layout
    op = layout.operator(OPERATOR_OT_name.bl_idname)
  3. Modify the property of the operators call.
    op.custom_prop = "Other Value."

Modifying Robert Gützkow's answer's code:

import bpy

class EXAMPLE_OT_multi_purpose(bpy.types.Operator):
    bl_idname = "example.multi_purpose"
    bl_label = "Function using variables."

    custom_prop = bpy.props.StringProperty(default="Default Value.")

    def execute(self, context):
        v = self.custom_prop
        self.report({'INFO'}, f"Prop is '{v}'")
        return {'FINISHED'}


class EXAMPLE_PT_panel(bpy.types.Panel):
    bl_label = "My own addon"
    bl_category = "Name of your tab"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        op = layout.operator(EXAMPLE_OT_multi_purpose.bl_idname, text="Function")
        op.custom_prop = "Default Value"

        op = layout.operator(EXAMPLE_OT_multi_purpose.bl_idname,
            text="Same Function, but different.")
        op.custom_prop = "Not default value, causing other behaviour."


classes = (EXAMPLE_OT_multi_purpose, EXAMPLE_PT_panel)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()
$\endgroup$
  • $\begingroup$ Thank you, I appreciate the information. My pain stems from the fact that my properties exist in my primary operator, which invokes a window. My scene elements have simple binary data, which gets converted into Blender properties when this operator window opens. I want to create page buttons in this window, which need to communicate back to the primary operator window (to sync the property data to the element's data page). I think my best option is to remove the properties from the operator window altogether, and place them somewhere else, like user preferences. $\endgroup$ – Robert Nov 18 '19 at 0:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.