Button click in basically connected with the operator calling in Blender user interface. However, some times actions, that need to be performed when a button is pressed, are quite simple and do not require a separate operator for them. And it makes no sense to fill a registered operators stack with a multitude of specific operators designed to perform one highly specialized function. It would be much more convenient to associate a button press with a separate function call but the Blender API allows to associate buttons only with an operator call.
To solve the problem of creating a separate operator for each button we can use the fact that the operator can be called with the input parameters.
Let’s create a simple operator class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from bpy.types import Operator from bpy.utils import register_class, unregister_class class TEST_OT_test_op(Operator): bl_idname = 'test.test_op' bl_label = 'Test' bl_description = 'Test' bl_options = {'REGISTER', 'UNDO'} def execute(self, context): return {'FINISHED'} def register(): register_class(TEST_OT_test_op) def unregister(): unregister_class(TEST_OT_test_op) |
and define here some separate functions:
- clear_scene – for removing all the meshes from the scene
- add_cube – for adding a cube
- add_sphere – for adding a sphere
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class TEST_OT_test_op(Operator): bl_idname = 'test.test_op' bl_label = 'Test' bl_description = 'Test' bl_options = {'REGISTER', 'UNDO'} def execute(self, context): return {'FINISHED'} @staticmethod def clear_scene(context): for obj in bpy.data.objects: bpy.data.objects.remove(obj) @staticmethod def add_cube(context): bpy.ops.mesh.primitive_cube_add() @staticmethod def add_sphere(context): bpy.ops.mesh.primitive_uv_sphere_add() |
Now the main feature – let’s add an input parameter of EnumProperty type for our operator.
1 2 3 4 5 6 7 |
action: EnumProperty( items=[ ('CLEAR', 'clear scene', 'clear scene'), ('ADD_CUBE', 'add cube', 'add cube'), ('ADD_SPHERE', 'add sphere', 'add sphere') ] ) |
According to this parameter value, we will call the desired function. The first item in the list is a text identifier, which will be used as a parameter value.
Modify the operator’s “execute” method to call the desired function depending on the “action” value:
1 2 3 4 5 6 7 8 |
def execute(self, context): if self.action == 'CLEAR': self.clear_scene(context=context) elif self.action == 'ADD_CUBE': self.add_cube(context=context) elif self.action == 'ADD_SPHERE': self.add_sphere(context=context) return {'FINISHED'} |
Now, if the operator is called with the “CLEAR” parameter, its “clear_scene” function will be executed, with the “ADD_CUBE” parameter – the “add_cube” function will be executed and so on.
The complete operator code looks the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import bpy from bpy.props import EnumProperty from bpy.types import Operator from bpy.utils import register_class, unregister_class class TEST_OT_test_op(Operator): bl_idname = 'test.test_op' bl_label = 'Test' bl_description = 'Test' bl_options = {'REGISTER', 'UNDO'} action: EnumProperty( items=[ ('CLEAR', 'clear scene', 'clear scene'), ('ADD_CUBE', 'add cube', 'add cube'), ('ADD_SPHERE', 'add sphere', 'add sphere') ] ) def execute(self, context): if self.action == 'CLEAR': self.clear_scene(context=context) elif self.action == 'ADD_CUBE': self.add_cube(context=context) elif self.action == 'ADD_SPHERE': self.add_sphere(context=context) return {'FINISHED'} @staticmethod def clear_scene(context): for obj in bpy.data.objects: bpy.data.objects.remove(obj) @staticmethod def add_cube(context): bpy.ops.mesh.primitive_cube_add() @staticmethod def add_sphere(context): bpy.ops.mesh.primitive_uv_sphere_add() def register(): register_class(TEST_OT_test_op) def unregister(): unregister_class(TEST_OT_test_op) |
Let’s create a tab in the N-panel with our custom sub-panel to place the buttons:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from bpy.types import Panel from bpy.utils import register_class, unregister_class class TEST_PT_panel(Panel): bl_idname = 'TEST_PT_panel' bl_label = 'Test' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Test' def draw(self, context): layout = self.layout layout.operator('test.test_op', text='Clear scene').action = 'CLEAR' layout.operator('test.test_op', text='Add cube').action = 'ADD_CUBE' layout.operator('test.test_op', text='Add sphere').action = 'ADD_SPHERE' def register(): register_class(TEST_PT_panel) def unregister(): unregister_class(TEST_PT_panel) |
In the “draw” method of our panel we defined three buttons: “Clear scene”, “Add cube” and “Add sphere”. And assigned each of them a call of our operator, but for each – with its own “action”: “CLEAR”, “ADD_CUBE “and” ADD_SPHERE”. As a result, when the button is pressed, the same operator is called, but with a different parameter to call different functions in accordance with this parameter value.
The final code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
import bpy from bpy.props import EnumProperty from bpy.types import Operator, Panel from bpy.utils import register_class, unregister_class class TEST_PT_panel(Panel): bl_idname = 'TEST_PT_panel' bl_label = 'Test' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Test' def draw(self, context): layout = self.layout layout.operator('test.test_op', text='Clear scene').action = 'CLEAR' layout.operator('test.test_op', text='Add cube').action = 'ADD_CUBE' layout.operator('test.test_op', text='Add sphere').action = 'ADD_SPHERE' class TEST_OT_test_op(Operator): bl_idname = 'test.test_op' bl_label = 'Test' bl_description = 'Test' bl_options = {'REGISTER', 'UNDO'} action: EnumProperty( items=[ ('CLEAR', 'clear scene', 'clear scene'), ('ADD_CUBE', 'add cube', 'add cube'), ('ADD_SPHERE', 'add sphere', 'add sphere') ] ) def execute(self, context): if self.action == 'CLEAR': self.clear_scene(context=context) elif self.action == 'ADD_CUBE': self.add_cube(context=context) elif self.action == 'ADD_SPHERE': self.add_sphere(context=context) return {'FINISHED'} @staticmethod def clear_scene(context): for obj in bpy.data.objects: bpy.data.objects.remove(obj) @staticmethod def add_cube(context): bpy.ops.mesh.primitive_cube_add() @staticmethod def add_sphere(context): bpy.ops.mesh.primitive_uv_sphere_add() def register(): register_class(TEST_OT_test_op) register_class(TEST_PT_panel) def unregister(): unregister_class(TEST_OT_test_op) unregister_class(TEST_PT_panel) if __name__ == '__main__': register() |
Now we got a mechanism for calling various functions by pressing buttons without creating and registering a set of operators for each of the buttons.