I saw this question this morning and i'm also quite interested in a Picker modal operator, so I did tinker a bit and achieve a somewhat beginning of a working code but I encountered some problems along the way.
The code is functional in 3Dview, as you can see it will print objects detected under the cursor, the problem is that it needs to work from any areas! so we need some context override action here
(Note that, as final touch, we could draw a little icon on screen with the name of the object picked at cursor location. Not sure if this is possible cross area ? i believe custom drawing are only allowed within the 3D view correct?)
import bpy
from bpy_extras import view3d_utils
# rework of this
# https://devtalk.blender.org/t/pick-material-under-mouse-cursor/6978/4
#We need the picker to work cross area, unfortunately blender don't like that very much
#So we need to override the context,
#the function below will try to find 3D view and update the given context.copy()
def find_3d_context(context_copy):
for area in bpy.context.window.screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
break
for space in area.spaces:
if space.type == 'VIEW_3D':
region_data = space.region_3d
break
print(area, region, region_data)
context_copy.update(
area=area,
region=region,
region_data=region_data,
)
return
def find_object(context, event):
# get the context arguments
scene = bpy.context.scene
region = bpy.context.region
rv3d = bpy.context.region_data
coord = event.mouse_region_x, event.mouse_region_y
scene = bpy.context.scene
viewlayer = bpy.context.view_layer
#this is the tricky part, region will not be found if we are not running this operator from a 3DVIEW
if rv3d is None: print("region_data not found") ; return
# get the ray from the viewport and mouse
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
ray_target = ray_origin + (view_vector *-10)
ray_goal = ray_origin + (view_vector *1000)
Rresult, Rlocation, Rnormal, Rindex, Robject, Rmatrix = scene.ray_cast(viewlayer.depsgraph, ray_target, ray_goal)
if Robject is not None:
print("Found an object ! : ",Robject)
return Robject
class ObjectPicker(bpy.types.Operator):
bl_idname = "view3d.test"
bl_label = "Picker test"
def modal(self, context, event):
#should work from any area, so we need to find correct context
context = context.copy()
find_3d_context(context)
object = find_object(context, event)
if (event.type == 'LEFTMOUSE'):
print("Execute Something With Object")
print("Done")
return {'CANCELLED'}
elif event.type in {'RIGHTMOUSE', 'ESC','RET'}:
print("Done")
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def register():
bpy.utils.register_class(ObjectPicker)
def unregister():
bpy.utils.unregister_class(ObjectPicker)
if __name__ == "__main__":
register()