I am trying to create an operator that works in the 3d view which constantly listens for mouse/keyboard events in the background and handles them.
I have found example code for an operator which mostly works, but I cannot completely achieve my goal because when it runs it always has the SpaceTextEditor
space in the context, presumably because I run it directly from the Text Editor. Since I am also trying to implement ray intersection to determine where on the Z plane the cursor is at a given time, I need access to the 3Dview in the context instead, and I need mouse coordinates that are relative to the 3D view and not the Text Editor.
How can I change the context so that it uses the 3D view instead of the text editor? I know that with a bit of additional code I can register the operator as a menu item and make it searchable in the F3 view, but I just always want the operator to run in the background, I don't want to having to enable it first.
My Operator code currently looks like the following:
import bpy
from bpy.types import Operator
import mathutils
from mathutils import Vector
from bpy_extras import view3d_utils
class TestOperator(bpy.types.Operator):
bl_idname = "object.test_tool"
bl_label = "Test Tool"
bl_options = {'REGISTER'}
def __init__(self):
print("Start")
def __del__(self):
print("End")
def execute(self, context):
if context != None and context.object != None:
context.object.location.x = self.x / 100.0
context.object.location.y = self.y / 100.0
def setpoint(self, contextOriginal, event):
context = contextOriginal.copy()
viewport_region = context.region
viewport_region_data = context.space_data.region_3d
viewport_matrix = viewport_region_data.view_matrix.inverted()
# Shooting a ray from the camera, through the mouse cursor towards the grid with a length of 100000
# If the camera is more than 100000 units away from the grid it won't detect a point
ray_start = viewport_matrix.to_translation()
ray_depth = viewport_matrix @ Vector((0,0,-100000))
# Get the 3D vector position of the mouse
ray_end = view3d_utils.region_2d_to_location_3d(viewport_region,viewport_region_data, (event.mouse_region_x, event.mouse_region_y), ray_depth )
# A triangle on the grid plane. We use these 3 points to define a plane on the grid
point_1 = Vector((0,0,0))
point_2 = Vector((0,1,0))
point_3 = Vector((1,0,0))
# Create a 3D position on the grid under the mouse cursor using the triangle as a grid plane
# and the ray cast from the camera
position_on_grid = mathutils.geometry.intersect_ray_tri(point_1,point_2,point_3,ray_end,ray_start,False )
# Place the empty on the grid under the mouse cursor
context.object.location = position_on_grid
def modal(self, context, event):
if event.alt and event.type == 'MOUSEMOVE' and context.object != None: # Apply
print('test')
self.setpoint(context, event)
elif event.type == 'LEFTMOUSE': # Confirm
return {'PASS_THROUGH'}
elif event.type in ('RIGHTMOUSE', 'ESC'): # Cancel
return {'PASS_THROUGH'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
self.x = event.mouse_region_x
self.y = event.mouse_region_y
self.execute(context)
print(context.window_manager.modal_handler_add(self))
return {'RUNNING_MODAL'}
# test call
bpy.utils.register_class(TestOperator)
bpy.ops.object.modal_operator('INVOKE_DEFAULT')
The Error I Get:
The above code results in this error message (when an object is selected, the alt key is held down, and the mouse is moved):
Python: Traceback (most recent call last):
File "/home/corner/cloud-sync/projects/3d-modeling/TestProject/testproject_005.blend/testproject_005.py", line 236, in modal
File "/home/corner/cloud-sync/projects/3d-modeling/TestProject/testproject_005.blend/testproject_005.py", line 208, in setpoint
AttributeError: 'SpaceTextEditor' object has no attribute 'region_3d'
The above line numbers refer to line 30 in the code block above, the line which says viewport_region_data = context.space_data.region_3d
Background:
The long term goal is to augment the user interface by adding additional elements in the viewport which the user can interact with - for example when a cube is selected it should render a smaller cube on each side of the cube, which when clicked would spawn a copy of the large cube at that location. Or if the smaller cube is dragged, the side of the cube is extruded in that direction.
Operator Sample code comes from the official documentation (https://docs.blender.org/api/blender2.8/bpy.types.Operator.html#modal-execution)
Ray Intersection code comes from https://devtalk.blender.org/t/mouse-position-on-gride-view/6095