user input operator

how do i get a user input like the rotate-operator. (3dview, select object, press ‘r’, type {x,y,z,1,2,3…,back_space})


So you want for example you press ‘W’ to be shown on the screen? If so, there is addon but not sure exactly where, search and you will find it in add-ons in User Preferences.

do you know what the addon does? any hint?

i could, of course catch every keystroke with a modal operator and

if event.type == 'w':
     foo()

but then i need to catch back_space, arrow keys and so on. …
write it to

context.area.header_text_set(self.offset)

i though, maybe there’s a build-in?

if i got you right, you wanna run a live operator like ‘R’ rotation. These live operators are written in C, and as far as i know, there is no way to call them from a python script.

The only current way appears to be a custom modal operator, that captures user input. Which will be way slower than all C live operators.

i started to do funny things like this:

import bpy
from mathutils import Vector
from bpy.props import StringProperty

numbers = ('ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE')
num_numbers = ('NUMPAD_0', 'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_5', 'NUMPAD_6', 'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9')

class ViewOperator(bpy.types.Operator):
    """Translate the view using mouse events"""
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple View Operator"

    offset = StringProperty(
            default="Rotation: |",
            name="Rotation:",
            )

    def modal(self, context, event):

        if event.type == 'MOUSEMOVE':
            context.area.header_text_set(self.offset)

        elif event.type == 'R' and event.value == 'RELEASE':
            bpy.ops.transform.rotate('INVOKE_DEFAULT', axis=(0,0,1))

        elif event.type in (numbers + num_numbers) and event.value == 'RELEASE':
            if event.type in numbers:
                num_int = numbers.index(event.type)
            else:
                num_int = num_numbers.index(event.type)
                
            self.offset = self.offset[:-1] + str(num_int) + self.offset[-1:]
            context.area.header_text_set(self.offset)
            
        elif event.type == 'BACK_SPACE' and event.value == 'RELEASE':
            if len(self.offset) > 11:
                self.offset = self.offset[:-2] + self.offset[-1:]
            context.area.header_text_set(self.offset)
            
        elif event.type == 'LEFTMOUSE':
            context.area.header_text_set()
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            context.area.header_text_set()
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):

        if context.space_data.type == 'VIEW_3D':
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}

        else:
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}


def register():
    bpy.utils.register_class(ViewOperator)


def unregister():
    bpy.utils.unregister_class(ViewOperator)


if __name__ == "__main__":
    register()

maybe someone can use the code…

but then i solved it different. long story…

thanks, really useful!

i took this a little further:

import bpy
from mathutils import Vector
from math import radians
from bpy.props import StringProperty

numbers = ('ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE')
num_numbers = ('NUMPAD_0', 'NUMPAD_1', 'NUMPAD_2', 'NUMPAD_3', 'NUMPAD_4', 'NUMPAD_5', 'NUMPAD_6', 'NUMPAD_7', 'NUMPAD_8', 'NUMPAD_9')

class ViewOperator(bpy.types.Operator):
    """Translate the view using mouse events"""
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple View Operator"

    offset = StringProperty(
            default="Rotation: |",
            name="Rotation:",
            )

    def modal(self, context, event):
        
        print(event.type, event.value)

        if event.type == 'MOUSEMOVE':
            context.area.header_text_set(self.offset)

        elif event.type == 'R' and event.value == 'RELEASE':
            bpy.ops.transform.rotate('INVOKE_DEFAULT', axis=(0,0,1))

        elif event.type in (numbers + num_numbers) and event.value == 'RELEASE':
            if event.type in numbers:
                num_int = numbers.index(event.type)
            else:
                num_int = num_numbers.index(event.type)
                
            self.offset = self.offset[:-1] + str(num_int) + self.offset[-1:]
            context.area.header_text_set(self.offset)
            
        elif event.type == 'BACK_SPACE' and event.value == 'RELEASE':
            if len(self.offset) > 11:
                self.offset = self.offset[:-2] + self.offset[-1:]
            context.area.header_text_set(self.offset)
            
        elif (event.type == 'LEFTMOUSE') or \
             (event.type in ('RET', 'NUMPAD_ENTER') and event.value == 'PRESS'):
                
            if len(self.offset) > 11:
                value = float(self.offset[10:-1])
                bpy.ops.transform.rotate(value=(radians(value),), axis=(0,0,1))
                context.area.header_text_set()
                return {'FINISHED'}
            else:
                context.area.header_text_set()
                return {'CANCELLED'}
    
        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            context.area.header_text_set()
            return {'CANCELLED'}


        return {'RUNNING_MODAL'}

    def invoke(self, context, event):

        if context.space_data.type == 'VIEW_3D':
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}

        else:
            self.report({'WARNING'}, "Active space must be a View3d")
            return {'CANCELLED'}


def register():
    bpy.utils.register_class(ViewOperator)


def unregister():
    bpy.utils.unregister_class(ViewOperator)


if __name__ == "__main__":
    register()

Also interesting is, that one can call a live operator from python, but i can’t make this work properly like: call live operator, wait until it finished, then do custom code. The live operator seems to get called multiple times (‘RUNNING_MODAL’), or the custom code won’t execute (‘CANCELLED’).

oh, there is a way to get user input, but not with modal operator. Calling to live operator from a standard operator (non-modal) with INVOKE_DEFAULT and not passing the value argument does the trick.

bpy.ops.transform.rotate('INVOKE_DEFAULT', axis=(0,0,1))

but then i solved it different.

bpy.ops.transform.rotate(‘INVOKE_DEFAULT’, axis=(0,0,1))

:slight_smile: that’s what i did. first i thought i can only do an ‘INVOKE_DEFAULT’ OR value=…, angle=…, axis=…
But then i tryed it with ‘INVOKE_DEFAULT’, axis=myaxis,… and it works!