Проектируя операторы, чаще всего мы предусматриваем возможность отмены выполненного ими действия – если после исполнения оператора пользователь нажмет комбинацию клавиш Ctrl + z, все воздействие нашего оператора на сцену должно быть отменено. Однако в некоторых случаях может понадобиться определить оператор, который будет игнорироваться при попытке отмены пользователем его действий. API в Blender предоставляет нам возможность создавать оба вида операторов, просто указывая возможность или запрет отмены в их параметрах.
Для примера создадим два оператора – один с возможностью отмены действия пользователем, и второй – без возможности отмены.
Определим простейший класс для оператора с возможностью отмены по Ctrl + z:
1 2 3 4 5 6 7 |
class TEST_OT_with_undo(bpy.types.Operator): bl_idname = 'test.with_undo' bl_label = 'Can UNDO' bl_options = {'REGISTER', 'UNDO'} def execute(self, context): return {'FINISHED'} |
Самое главное здесь для нас – это строка с параметром bl_options. Именно в ней указывается, будет ли отменяться действие оператора. А именно, за отмену действий отвечает значение “UNDO” в указываемом сете значений.
1 |
bl_options = {'REGISTER', 'UNDO'} |
В данном случае значение “UNDO” присутствует – это значит, что действия оператора будут отменяться при нажатии пользователем Ctrl + z.
В функции execute(), для примера, напишем код, который переставляет выбор на один случайный объект в сцене.
Полный код оператора будет следующим:
1 2 3 4 5 6 7 8 9 10 11 12 |
class TEST_OT_with_undo(bpy.types.Operator): bl_idname = 'test.with_undo' bl_label = 'Can UNDO' bl_options = {'REGISTER', 'UNDO'} def execute(self, context): non_selected = [obj for obj in bpy.data.objects if not obj.select_get()] random_select = random.choice(non_selected) for obj in context.selected_objects: obj.select_set(False) random_select.select_set(True) return {'FINISHED'} |
Теперь определим похожий оператор, но без возможности отмены действия.
1 2 3 4 5 6 7 |
class TEST_OT_without_undo(bpy.types.Operator): bl_idname = 'test.without_undo' bl_label = 'No UNDO' bl_options = {'REGISTER'} def execute(self, context): return {'FINISHED'} |
Главное отличие этого оператора от предыдущего в том, что мы не указали значение “UNDO” в сете в параметре bl_options. Таким образом, данный оператор будет игнорировать попытки отмены действия пользователем.
Пусть, например, данный оператор переключает отображение случайного объекта в 3D вьюпорте в режим сетки “Wireframe”.
Полный код оператора:
1 2 3 4 5 6 7 8 9 10 11 12 |
class TEST_OT_without_undo(bpy.types.Operator): bl_idname = 'test.without_undo' bl_label = 'No UNDO' bl_options = {'REGISTER'} def execute(self, context): non_wire = [obj for obj in bpy.data.objects if obj.display_type != 'WIRE'] random_select = random.choice(non_wire) for obj in bpy.data.objects: obj.display_type = 'TEXTURED' random_select.display_type = 'WIRE' return {'FINISHED'} |
Не забудьте зарегистрировать оба оператора в Blender Python API.
1 2 |
bpy.utils.register_class(TEST_OT_with_undo) bpy.utils.register_class(TEST_OT_without_undo) |
Теперь создадим тестовую пользовательскую панель с двумя кнопками, для вызова первого и второго операторов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class TEST_PT_panel(bpy.types.Panel): bl_idname = 'TEST_PT_panel' bl_label = 'UNDO TEST' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'TEST' def draw(self, context): self.layout.operator( operator='test.with_undo' ) self.layout.operator( operator='test.without_undo' ) |
Так же зарегистрируем ее в Blender API, чтобы она появилась в N-панели в 3D вьюпорте.
1 |
bpy.utils.register_class(TEST_PT_panel) |
Добавим несколько мешей в сцену.
А теперь попробуем несколько раз нажать на кнопку “Can UNDO”, нажатие на которую вызывает наш первый оператор с возможностью отмены. Мы видим, как выбор переключается с меша на меш. И если мы теперь нажмем комбинацию клавиш Ctrl + z, мы увидим, как выбор отменяется – переключается в обратном направлении, пока не дойдет до исходного состояния.
Теперь, если мы несколько раз нажмем на кнопку “No UNDO”, для переключения режима отображения в “сетку” для случайного меша, а потом попытаемся нажать Ctrl + z, мы не увидим никакого эффекта возврата. Все потому, что операции отмены для данного оператора мы не предусмотрели.