I wrote a simple add-on to enable canvas flipping in Blender (for Grease Pencil) with a hotkey, as well as a menu button. All it does is multiply the camera's local X scale by -1:

current_scene = bpy.context.scene;
active_camera = current_scene.camera;
active_camera.scale.x *= -1;

This basically works. However, if I:

  1. flip the canvas
  2. draw a Grease Pencil stroke
  3. undo with Ctrl+Z

the last stroke disappears as expected, but the canvas also flips back. Somehow both actions are interpreted as one single operation.

(This code is based on a brief description of a similar functionality in the GP Toolbox add-on, which has the same problem.)

Why are the actions combined? Did I miss a step in the code?

Full add-on code below.

bl_info = {
    "name": "Flip Canvas",
    "author": "Latin1",
    "version": (1, 0),
    "blender": (4, 1, 1),
    "location": "Scene",
    "description": "Inverts the camera's x axis",
    "warning": "",
    "doc_url": "",
    "category": "Scene",

import bpy;

def flip_canvas_btn(context):
    current_scene = bpy.context.scene;
    active_camera = current_scene.camera;
    active_camera.scale.x *= -1;

class Flip_Canvas(bpy.types.Operator):
    bl_idname = "latin1.flip_canvas";
    bl_label = "Flip canvas";

    def execute(self, context):
        return {'FINISHED'};

def menu_func(self, context):
    self.layout.operator(SimpleOperator.bl_idname, text=SimpleOperator.bl_label);

# ----------------------------------------------------------------------------------------
class LayoutDemoPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Flip the canvas"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout;

        scene = context.scene;

        # Interface button
        row = layout.row();
        row.scale_y = 1.0;

def register():

def unregister():

if __name__ == "__main__":
  • $\begingroup$ Blender version 4.1.1. As mentioned, it simply negates the camera's X scale. My add-on adds a button to the interface, and a key binding. $\endgroup$
    – Latin1
    Commented May 4 at 0:55
  • $\begingroup$ The code works fine in the Python console, or when selecting the camera and changing the X scale manually in the UI. But as an add-on, which I only created to link the code to a keyboard shortcut, the problem is entirely consistent and reproducible. $\endgroup$
    – Latin1
    Commented May 4 at 2:31
  • $\begingroup$ I added the source code to the original post. It should be installable as an add-on now if anyone wants to test it. (Omit the ``` at the end; not sure why that's displaying in the code.) $\endgroup$
    – Latin1
    Commented May 4 at 2:37

1 Answer 1


That is because you did not register your operator to be included into the UNDO history. You can do that by adding bl_options = {'REGISTER', 'UNDO'}

class Flip_Canvas(bpy.types.Operator):
    bl_idname = "latin1.flip_canvas";
    bl_label = "Flip canvas";
    bl_options = {'REGISTER', 'UNDO'}
  • 1
    $\begingroup$ And there it is. Thank you, the canvas flip is now its own, unique action. I'm now realising that in the ideal workflow, the canvas flip wouldn't even register as an action, remaining independent from the undo stack; but in this case your solution is indeed exactly what I was asking for. $\endgroup$
    – Latin1
    Commented May 4 at 3:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .