I'd like to allow for an unlimited mousemove delta value in my python add-on, similar to how you can press G to move the default cube in blender and you can move your mouse outside the 3d view and it will keep scrolling (when the cursor goes off screen, it will reappear on the other side of the screen etc etc and not be limited by the edges of the screen), and it will keep adding to the translate value of the cube.

In the Text Editor python templates there is usually something like

delta = (event.mouse_y - self.init_mouse_y) / 1

(for example the Modal Operator template)

With this code, the mouse is limited by the edges of the screen. Does anybody know how to make the value change continuous? Thanks!


1 Answer 1


Method 1: bl_options

bl_options = {'GRAB_CURSOR', 'BLOCKING'}

Method 2: cursor_warp

You can know the total travel from the cube offset

import bpy
from bpy.props import IntProperty, FloatProperty

class ModalOperator(bpy.types.Operator):
    """Move an object with the mouse, example"""
    bl_idname = "object.modal_operator"
    bl_label = "Simple Modal Operator"

    last_mouse_x: IntProperty()
    first_value: FloatProperty()

    def modal(self, context, event):
        if event.type == 'MOUSEMOVE':
            x = event.mouse_x

            delta = x - self.last_mouse_x
            if abs(delta) > self.w_half:
                delta = 0
            context.object.location.x += delta * 0.002

            # wrap cursor x only
            if x < 200:
                x = self.w_width - 20
                self.cursor_warp(x, event.mouse_y)
            elif x > self.w_width:
                x = 220
                self.cursor_warp(x, event.mouse_y)

            self.last_mouse_x = x

        elif event.type == 'LEFTMOUSE':
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            context.object.location.x = self.first_value
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.object:
            self.last_mouse_x = event.mouse_x
            self.first_value = context.object.location.x

            # get window height, width
            window = context.window
            self.w_width = window.width - 200
            self.w_half = self.w_width // 2
            self.cursor_warp = context.window.cursor_warp

            return {'RUNNING_MODAL'}
            self.report({'WARNING'}, "No active object, could not finish")
            return {'CANCELLED'}

def register():

def unregister():

if __name__ == "__main__":

    # test call
  • $\begingroup$ Thank you, in my case I was making a Gizmo / GizmoGroup add-on, so I had to use gz.use_grab_cursor = True $\endgroup$ Commented Dec 30, 2022 at 13:58

You must log in to answer this question.

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