Defining operators, we most often provide for the ability to undo the action they performed – if after executing the operator the user presses the Ctrl + z key combination, all the effects of the operator on the scene should be canceled. However, in some cases, it may be necessary to define an operator that will be ignored if the user tries to undo its actions. Blender’s API allows us to create both types of operators by simply specifying the ability or prohibition of undo in their parameters.
For example, let’s create two operators – the first with the ability to undo by the user, and the second – without the ability to undo.
Define the simplest class for an operator with the ability to cancel by 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'} |
The most important thing here for us is the line with the “bl_options” parameter. Whether the operator’s action will be canceled specifies in it. The “UNDO” value in the specified set of values is responsible for canceling.
1 |
bl_options = {'REGISTER', 'UNDO'} |
In this case, the value “UNDO” exists – this means that the operator’s actions will be canceled when the user presses Ctrl + z.
In the execute() function, for example, we can write code that switches the selection to one random object in the scene.
The full operator code will be as follows:
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'} |
Now define a similar operator, but without the ability to cancel the action.
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'} |
The main difference between this operator and the previous one is that we did not specify the “UNDO” value in the “bl_options” parameter. Thus, this operator will ignore attempts to cancel its action by the user.
For example, let’s say this operator switches the display of a random object in the 3D viewport to the “Wireframe” mode.
Full operator code:
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'} |
Don’t forget to register both operators with the Blender Python API.
1 2 |
bpy.utils.register_class(TEST_OT_with_undo) bpy.utils.register_class(TEST_OT_without_undo) |
Now let’s create a test user panel with two buttons to call the first and second operators.
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' ) |
Also register it in Blender API so that it appears in the N-panel in the 3D viewport.
1 |
bpy.utils.register_class(TEST_PT_panel) |
Add several meshes to the scene.
Now let’s try to press the “Can UNDO” button several times. It calls our first operator with the ability to cancel. We see the selection switching from mesh to mesh. And if we now press the Ctrl + z key combination, we will see the selection being cancelled – switching in the opposite direction until it reaches the initial state.
Now, if we press the “No UNDO” button several times to switch the display mode to “grid” for a random mesh, and then try to press Ctrl + z, we will not see any return effect. This is because we did not provide the undo ability for this operator.