Can the WorkSpaceTool be used to start a modal Operator which responds to mouse events?

I came across the script for WorkSpaceTool in the Python Templates and thought it would be a good starting point for developing a tool. My end goal is to have a tool which when selected will allow the user to click and drag with the mouse to create a cube. Unforunately, I am finding that WorkSpaceTool just creates a new instance of my operator for every mouse event and then calls the invoke() method - even for mouse move events and event if my operator is returning RUNNING_MODAL. modal() never gets called.

I can find no information about how WorkSpaceTool is supposed to be used. Is it possible to use this to start a modal operator running which then interactively handle mouse input?

import bpy



class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    def __init__(self):
        print("construct SimpleOperator")

    def __del__(self):
        print("destruct SimpleOperator")

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def modal(self, context, event):
        print("modal evTyp:%s evVal:%s" % (str(event.type), str(event.value)))
        context.area.tag_redraw()

        if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
            # allow navigation
            return {'PASS_THROUGH'}
        
        elif event.type == 'MOUSEMOVE':
            
            return {'RUNNING_MODAL'}

        elif event.type == 'LEFTMOUSE':
            mouse_pos = (event.mouse_region_x, event.mouse_region_y)
            print("  pos %s" % str(mouse_pos))
            
            return {'RUNNING_MODAL'}

        elif event.type == 'RIGHTMOUSE':
            mouse_pos = (event.mouse_region_x, event.mouse_region_y)
            print("  pos %s" % str(mouse_pos))
            
            return {'FINISHED'}

    def execute(self, context):
        print("execute SimpleOperator")
        return {'FINISHED'}

    def invoke(self, context, event):
        print("invoke evTyp:%s evVal:%s" % (str(event.type), str(event.value)))
        
        
        return {'RUNNING_MODAL'}


#-------------------------------------


class MyTool(bpy.types.WorkSpaceTool):
    bl_space_type = 'VIEW_3D'
    bl_context_mode = 'OBJECT'

    # The prefix of the idname should be your add-on name.
    bl_idname = "my_template.my_circle_select"
    bl_label = "My Circle Select"
    bl_description = (
        "This is a tooltip\n"
        "with multiple lines"
    )
    bl_icon = "ops.generic.select_circle"
    bl_widget = None
    bl_keymap = (
        ("object.simple_operator", {"type": 'LEFTMOUSE', "value": 'PRESS'},
         {"properties": []}),
    )

    def draw_settings(context, layout, tool):
        props = tool.operator_properties("view3d.select_circle")
        layout.prop(props, "mode")
        layout.prop(props, "radius")

#---------------------------


def register():
    bpy.utils.register_class(SimpleOperator)
    bpy.utils.register_tool(MyTool, after={"builtin.scale_cage"}, separator=True, group=True)
    

def unregister():
    bpy.utils.unregister_class(SimpleOperator)
    bpy.utils.unregister_tool(MyTool, after={"builtin.scale_cage"}, separator=True, group=True)

if __name__ == "__main__":
    register()

Hey there,

It looks like there’s something missing in the operators invoke function, a modal operator has to register itself otherwise the modal function wont be called. You should check the modal operator template for more details.

You’re right. I added that and it worked.

I have a related question. I am creating a tool similar to the Bevel tool. It adds points to the object. How do I get behavior similar to the Redo Panel where moving the mouse can adjust a modal operator’s property but the points only get added once? Thank you!