[PYTHON] [Blender] How to handle mouse and keyboard events in Blender scripts

If you are making a Blender plugin, you will want to handle events from the mouse and keyboard and provide more interactive features. The function to change the size of an object just by moving the mouse when pressing the S key in Object mode is just an example of handling an event from the mouse, and the operation feeling is good.

So this time, I will show you how to handle mouse and keyboard events in Blender script with samples.

sample

Again, the sample is shown first. This sample allows you to scale the object according to the mouse position. We have dealt with the following events so that you can understand the pattern of input events in Blender.

Source code

mouse_keyboard_event_test.py


import bpy

bl_info = {
    "name" : "Mouse/Keyboard event test",
    "author" : "Nutti",
    "version" : (0,1),
    "blender" : (2, 7, 0),
    "location" : "UV > Mouse/Keyboard event test",
    "description" : "Mouse/Keyboard event test.",
    "warning" : "",
    "wiki_url" : "",
    "tracker_url" : "",
    "category" : "UV"
}

running = False     #True if event acquisition is in progress

class MouseKeyboardEventTest(bpy.types.Operator):
    """Mouse/Keyboard event test."""
    
    bl_idname = "uv.mouse_keyboard_event_test"
    bl_label = "Mouse/Keyboard event test"
    bl_description = "Mouse/Keyboard event test"
    bl_options = {'REGISTER', 'UNDO'}
    
    obj_scale = 1.0     #Object magnification
    orig_x = 0.0        #X coordinate when the button is pressed
    orig_scale = []     #Object magnification when the button is pressed

    def modal(self, context, event):
        global running
    
        #Exit if not working
        if running is False:
            return {'PASS_THROUGH'}
        
        #Apply magnification to objects
        active_obj = bpy.context.active_object
        active_obj.scale[0] = self.orig_scale[0] * self.obj_scale
        active_obj.scale[1] = self.orig_scale[1] * self.obj_scale
        active_obj.scale[2] = self.orig_scale[2] * self.obj_scale
        
        #############################################
        #mouse/Processing to handle keyboard events
        #############################################
        
        #Mouse movement-Set the magnification of the object based on the X coordinate of the mouse
        if event.type == 'MOUSEMOVE':
            factor = 1.0 / 100.0
            self.obj_scale = 1.0 + (event.mouse_x - self.orig_x) * factor
            return {'RUNNING_MODAL'}
        #When you press the spacebar-Return the magnification when the button is pressed
        elif event.type == 'SPACE':
            if event.value == 'PRESS':
                self.orig_x = event.mouse_x
                return {'RUNNING_MODAL'}
        #When right click is pressed-Confirm the changed enlargement ratio and end the operation
        if event.type == 'RIGHTMOUSE':
            if event.value == 'PRESS':
                running = False
                return {'FINISHED'}
        
        return {'RUNNING_MODAL'}
    
    def invoke(self, context, event):
        global running
        
        if context.area.type == 'VIEW_3D':
            #Accept each event if the button is pressed while it is not running
            if running is False:
                running = True
                #Initial state setting
                self.obj_scale = 1.0
                self.orig_x = event.mouse_x
                active_obj = bpy.context.active_object
                self.orig_scale = [
                    active_obj.scale[0],
                    active_obj.scale[1],
                    active_obj.scale[2]
                ]
                #modal handler settings
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
        return {'CANCELLED'}
        
# 'N'Place a button in the menu that appears on the right side of VIEW 3D when you press a key
class OBJECT_PT_MKET(bpy.types.Panel):
    bl_label = "Mouse/Keyboard Event Test"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    def draw(self, context):
        sc = context.scene
        layout = self.layout
        #Button placement
        layout.operator(MouseKeyboardEventTest.bl_idname, text="Start", icon="PLAY")


def register():
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()

How to use

Follow the steps below to install the script. Blender Wiki

If you click the "Start" button in "Mouse / Keyboard Event Test" of the menu displayed on the right side when you press the'N'key in View 3D, the following operations will be accepted.

operation Behavior on objects
Mouse movement Move the mouse to the right of the screen from where you clicked the button to enlarge the selected object, and move it to the left of the screen to zoom out.
Space bar Restores the size of the object when the button is clicked.
right click Confirm the size of the changed object and finish the operation.

Sample explanation

The basic explanation of the Blender script is introduced in the following article, so here we will focus on the explanation of the newly added elements. [\ Blender ] How to make a Blender plugin

Button registration

Add a button to the menu on the right that appears when you press the'N'key in View 3D. Button registration is beyond the scope of this article, so think of it like this for now.

# 'N'Place a button in the menu that appears on the right side of VIEW 3D when you press a key
class OBJECT_PT_MKET(bpy.types.Panel):
    bl_label = "Mouse/Keyboard Event Test"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    def draw(self, context):
        #Button placement
        # ...

invoke method

** A method that is called when the class is called **. In this sample, it is called when the button is clicked.

    def invoke(self, context, event):
        global running
        
        if context.area.type == 'VIEW_3D':
            #Accept each event if the button is pressed while it is not running
            if running is False:
                running = True

                #Initial state setting
                # ....

                #modal handler settings
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
        return {'CANCELLED'}

We have specified a handler (an object with a modal method) in the context.window_manager.modal_handler_add function to accept mouse events after clicking the button. Here, specify self because the class has both ʻinvoke and modal` methods at the same time. In addition, various initial state settings are also made.

If you set a handler here, the modal method will continue to be called ** after **, so we have prepared a running variable to eliminate operations on the object even if an event occurs at the time of stop.

modal method

If you register a handler with the context.window_manager.modal_handler_add function in the ʻinvoke` method, it will be called ** when an event occurs from the mouse or keyboard **.

    def modal(self, context, event):
        global running

        #Exit if not working
        if running is False:
            return {'PASS_THROUGH'}

        # ....
        
        #Mouse movement-Set the magnification of the object based on the X coordinate of the mouse
        if event.type == 'MOUSEMOVE':
            factor = 1.0 / 100.0
            self.obj_scale = 1.0 + (event.mouse_x - self.orig_x) * factor
            return {'RUNNING_MODAL'}
        #When you press the spacebar-Return the magnification when the button is pressed
        elif event.type == 'SPACE':
            if event.value == 'PRESS':
                self.orig_x = event.mouse_x
                return {'RUNNING_MODAL'}
        #When right click is pressed-Confirm the changed enlargement ratio and end the operation
        if event.type == 'RIGHTMOUSE':
            if event.value == 'PRESS':
                running = False
                return {'FINISHED'}
        
        return {'RUNNING_MODAL'}

The modal method handles each event as follows.

Event processing
Mouse movement Gets the current position and calculates the lateral difference between the current position and the initial position to set the magnification of the object.
afterwards,{'RUNNING_MODAL'}Is returned and processing continues.
right click {'FINISHED'}Returns and finishes the process.
Space bar Change the enlargement ratio of the object to the initial value.
afterwards,{'RUNNING_MODAL'}Is returned and processing continues.
Other {'RUNNING_MODAL'}Is returned and processing continues.

At the end

I introduced how to handle events from the mouse and keyboard in Blender scripts. By knowing keyboard and mouse events, I think that ** more things can be achieved with scripts **, so why not try using it?

Reference information

Recommended Posts

[Blender] How to handle mouse and keyboard events in Blender scripts
How to handle session in SQLAlchemy
[Blender] How to make Blender scripts multilingual
How to execute external shell scripts and commands in python
How to handle Japanese in Python
How to do Server-Sent Events in Django
How to use is and == in Python
How to write the correct shebang in Perl, Python and Ruby scripts
How to generate permutations in Python and C ++
Data cleaning How to handle missing and outliers
How to write async and await in Vue.js
How to handle datetime type in python sqlite3
How to plot autocorrelation and partial autocorrelation in python
How to know the current directory in Python in Blender
How to define Decorator and Decomaker in one function
How to right click using keyboard input in RPA?
[Python] How to sort dict in list and instance in list
How to use Decorator in Django and how to make it
How to handle JSON in Ruby, Python, JavaScript, PHP
How to get RGB and HSV histograms in OpenCV
[Python] How to handle inf and NaN in numpy mean, standard deviation, maximum / minimum
Script to count and stop up to 5 seconds in Python in Blender
Foreigners talk: How to name classes and methods in English
How to use pyenv and pyenv-virtualenv in your own way
[Introduction to Udemy Python 3 + Application] 36. How to use In and Not
How to create and use static / dynamic libraries in C
Comparison of how to use higher-order functions in Python 2 and 3
How to get all the keys and values in the dictionary
[TF] How to load / save Model and Parameter in Keras
How to develop in Python
How to handle data frames
How to create dataframes and mess with elements in pandas
How to log in to AtCoder with Python and submit automatically
How to copy and paste command line content without mouse in bash on Linux or mac
How to set the output resolution for each keyframe in Blender
How to auto-update App Store description in Google Sheets and Fastlane
How to handle multiple versions of CUDA in the same environment
How to install OpenCV on Cloud9 and run it in Python
How to compare lists and retrieve common elements in a list
Repeated @ app.callback in Dash How to write Input and State neatly
How to give and what the constraints option in scipy.optimize.minimize is
How to use functions in separate files Perl and Python versions
<Pandas> How to handle time series data in a pivot table
[ROS2] How to describe remap and parameter in python format launch
How to display bytes in the same way in Java and Python
[Python] How to do PCA in Python
How to use classes in Theano
How to write soberly in pandas
How to collect images in Python
How to update Spyder in Anaconda
How to reflect CSS in Django
How to install and configure blackbird
How to use .bash_profile and .bashrc
How to kill processes in bulk
How to install and use Graphviz
How to use Mysql in python
How to wrap C in Python
How to use ChemSpider in Python
[Blender] How to make a Blender plugin
How to use PubChem in Python
How to run TensorFlow 1.0 code in 2.0