К нажатию кнопки в пользовательском интерфейсе обычно привязывают вызов нужного оператора. Однако часто действия, которые нужно выполнить при нажатии на кнопку, достаточно просты и не требуют оформления в виде отдельного оператора. Да и забивать стек зарегистрированных операторов множеством специфических операторов, рассчитанных на выполнение одной конкретной узкоспециализированной функции не имеет смысла. Гораздо удобнее было бы связать нажатие кнопки с вызовом отдельной функции, однако API Blender позволяет связывать кнопки только с вызовом оператора.
Обойти проблему создания отдельного оператора под каждую кнопку можно, воспользовавшись тем, что оператор можно вызывать с указанием входных параметров.
Для примера создадим простейший оператор:
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) |
Определим в нем несколько нужных нам функций:
- clear_scene – для удаления всех объектов из сцены
- add_cube – для добавления в сцену куба
- add_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() |
Теперь самое главное – добавим в оператор входной параметр action перечислимого типа EnumProperty:
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') ] ) |
в зависимости от значения которого будем вызывать нужную функцию. Первым в списке идет текстовый идентификатор, который и будет передаваться в оператор при вызове.
Добавим в функцию execute оператора условие для вызова нужной функции в зависимости от значения входного параметра acton:
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'} |
Теперь если оператор будет вызван с параметром “CLEAR” – будет выполнена его функция clear_scene, с параметром “ADD_CUBE” – будет выполнена функция add_cube и т.д.
Полный код оператора выглядит так:
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) |
В N-панели создадим свою вкладку с пользовательской панелью для размещения наших кнопок:
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) |
В методе draw созданного класса панели мы определили три кнопки “Clear scene”, “Add cube” и “Add sphere”, привязав к каждой из них вызов созданного ранее оператора, но для каждой – со своим параметром action равным “CLEAR”, “ADD_CUBE” и “ADD_SPHERE”. В итоге при нажатии на кнопку вызывается один и тот же оператор, но с разным входным параметром, в результате чего оператор выполняет разные функции в соответствии с полученным значением этого параметра.
Финальный код выглядит следующим образом:
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() |
В результате мы получили механизм для вызова различных функций по нажатию кнопок без необходимости создавать и регистрировать для каждой из кнопок отдельный оператор.
Привет,как ты сделал ходной параметр action перечислимого типа EnumProperty?
Мне нужно сделать такие же кнопки,но только для управления анимацией
В примере как раз показано добавление входного параметра типа EnumProperty. Поставьте свои значения и в соответствии с ними вызывайте нужные функции.