i am making a custom tool, i would like to have different behaviour if i use the tool holding Alt, Ctrl, Shift.... in the templates there is an Half/example in the UItool simple. But i cant make it work in my own script.
bl_keymap = ( ("view3d.select_circle", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("wait_for_input", False)]}),
("view3d.select_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'SUB'), ("wait_for_input", False)]}),
)
These lines keymap the tool 2 times, to the same "operator", But setting the properties: Wait_for_input and mode. So if i understand correctly i can add a property to my operator to choose if create cubes or sphere . and i should be able to set this property from here.
What i am messing around ?
Here the full script, You need to run it you have to add a cube in the scene and 2 circle named GRABBER and GRABBER2 the tool will move the GRABBER to the nearest vertex under mouse.
Sorry for long script, I think that the wrong part is the ( * Tool ) or the ( * modal operator ) The result i get now is a empty string for my_mode and a 0 for my_mode2 both are two property i added in the operator, if i inizialize them in the invoke, i can get a result. (commented now) but i never get the 1 and B i am calling with the tool.
Thanks for your time
import bpy
from bpy.props import (StringProperty,
BoolProperty,
IntProperty,
)
from bpy.types import (Panel,
Menu,
Operator,
PropertyGroup,
)
from bpy.types import WorkSpaceTool
from bpy_extras import view3d_utils
#
# TOOL
class CustomTool(WorkSpaceTool):
bl_space_type = 'VIEW_3D'
bl_context_mode = 'OBJECT'
bl_idname = "grabber.mycustom_tool"
bl_label = " GRABBER "
bl_description = (" move tool ")
bl_icon = "ops.gpencil.draw.poly"
bl_widget = None
bl_keymap = (
("wm.modal_grabber", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("my_mode2", 1 )]}),
("wm.modal_grabber", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("my_mode", "B")]}),
)
#
# + SCENE PROPERTY
class Custom_PROP(PropertyGroup):
MouseL: BoolProperty()
bpy.utils.register_class(Custom_PROP) # REGISTER THE PROPERTY SO I CAN USE IT NOW
bpy.types.Scene.CUSTOM_DATA = bpy.props.PointerProperty(type=Custom_PROP) # ADD THE CUSTOM PROPERTY IN THE SCENE
#
# PANEL
class CUSTOM_Panel(Panel):
bl_label = "Mousetester"
bl_idname = "OBJECT_PT_custom_mouse"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop( context.scene.CUSTOM_DATA , "MouseL" )
row = layout.row()
row.label( text= "Read the print value in console " )
row.label( text= " Dont know why it dont update" )
#
# OPERATOR MODAL
class CUSTOM_GRABBER(bpy.types.Operator):
bl_idname = "wm.modal_grabber"
bl_label = "test Modal grabber"
my_mode2: bpy.props.IntProperty(name="MODES_INT")
my_mode: bpy.props.StringProperty(name="MODES_STR")
def invoke(self, context, event):
print("INVOKE")
# self.my_mode2 = 66
# self.my_mode = "SEEI"
self.execute(context)
return {'RUNNING_MODAL'}
def modal(self, context, event):
if event.type == 'MOUSEMOVE':
print("Mouse moved to " + str(event.mouse_region_x) + " " + str(event.mouse_region_y) )
print (bpy.context.scene.CUSTOM_DATA.MouseL)
getnearestvertex(context, event)
if self.my_mode =="A":
bpy.data.objects["GRABBER2"].location=bpy.data.objects["GRABBER"].location
context.area.tag_redraw()
print("MY MODE IS = this")
print("mode = " + self.my_mode)
print("mode2 = "+ str(self.my_mode2) )
if event.type == "LEFTMOUSE" :
if event.value == "PRESS": # THIS WILL NOT BE CALLED
bpy.context.scene.CUSTOM_DATA.MouseL =1 #
print("MOUSE_LEFT---PRESS") #
print (bpy.context.scene.CUSTOM_DATA.MouseL) #
if event.value == "RELEASE":
bpy.context.scene.CUSTOM_DATA.MouseL =0
print("MOUSE_LEFT---RELEASE")
print (bpy.context.scene.CUSTOM_DATA.MouseL)
context.area.tag_redraw()
return {'CANCELLED'}
if event.value == "CLICK":
print("MOUSE_LEFT---CLICK")
if event.type == 'ESC' :
return {'CANCELLED'}
return {'PASS_THROUGH'}
def execute(self, context):
wm = context.window_manager
wm.modal_handler_add(self)
print("THIS IS THE FIRST CLICK ") # FIRST CLICK
bpy.context.scene.CUSTOM_DATA.MouseL =1 # So i set the value here
print (bpy.context.scene.CUSTOM_DATA.MouseL)
context.area.tag_redraw() # UPDATE
return {'RUNNING_MODAL'}
#
# GET NEAREST VERTEX
def getnearestvertex(context, event):
scene = context.scene
region = context.region
rv3d = context.region_data
coord = event.mouse_region_x, event.mouse_region_y
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
def visible_objects_and_duplis():
depsgraph = context.evaluated_depsgraph_get()
for dup in depsgraph.object_instances:
if dup.is_instance: # Real dupli instance
obj = dup.instance_object
yield (obj, dup.matrix_world.copy())
else: # Usual object
obj = dup.object
yield (obj, obj.matrix_world.copy())
def obj_ray_cast(obj, matrix):
matrix_inv = matrix.inverted()
ray_origin_obj = matrix_inv @ ray_origin
ray_target_obj = matrix_inv @ ray_target
ray_direction_obj = ray_target_obj - ray_origin_obj
success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)
if success:
return location, normal, face_index
else:
return None, None, None
# cast rays and find the closest object
best_length_squared = -1.0
best_obj = None
for obj, matrix in visible_objects_and_duplis():
if obj.type == 'MESH':
hit, normal, face_index = obj_ray_cast(obj, matrix)
if hit is not None:
hit_world = matrix @ hit
# scene.cursor.location = hit_world # DO NOT MOVE THE CURSOR
length_squared = (hit_world - ray_origin).length_squared
if best_obj is None or length_squared < best_length_squared:
best_length_squared = length_squared
best_obj = obj
# now we have the object under the mouse cursor,
# we could do lots of stuff but for the example just select.
if best_obj is not None:
# for selection etc. we need the original object,
# evaluated objects are not in viewlayer
best_original = best_obj.original
QQQ=best_original
vertex_n = len( [v for v in QQQ.data.vertices if v.select ] )
min_dist=50.0
objmatrix = QQQ.matrix_world
for v in QQQ.data.vertices:
world_xyz = objmatrix @ v.co
vector_diff = world_xyz - hit_world
vector_length = vector_diff.length
if vector_length < min_dist :
min_dist = vector_length
closer_vertex = world_xyz
bpy.data.objects["GRABBER"].location=closer_vertex
# bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
#
#
#
# REGISTRATION
classes = ( # I ALREADY REGISTERED THE PROPERTY GROUP
# Custom_PROP ,
CUSTOM_Panel,
CUSTOM_GRABBER
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.utils.register_tool( CustomTool , after={"builtin.scale_cage"}, separator=True, group=True)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
bpy.utils.unregister_tool( CustomTool )
if __name__ == "__main__":
register()