I've created a modal operator that I currently start by pressing a button in a panel, but want to adapt it so that I can use it from a workspace tool button instead. I've written a test script that creates my button and can send commands to an operator - but the trouble is that the operator acts like a one-off operator and not a modal one. Only the invoke
method seems to be called. The modal
method never is even though the operator is returning RUNNING_MODAL. How can I adapt this to create a modal operator that maintains state for as long as I'm using the tool?
import bpy
import bpy.utils.previews
class TerrainSculptWorkspaceTool(bpy.types.WorkSpaceTool):
bl_space_type = 'VIEW_3D'
bl_context_mode = 'OBJECT'
bl_idname = "kitfox_terrain.terrain_brush_draw"
bl_label = "Terrain Brush Draw"
bl_description = ("Raise or lower terrain under cursor to the current brush height.")
bl_icon = "ops.gpencil.draw.poly"
bl_widget = None
bl_keymap = (
("kitfox.echo_tool", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("tool_mode", "DEFAULT" )]}),
("kitfox.echo_tool", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties": [("tool_mode", "CONTROL")]}),
("kitfox.echo_tool", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True},
{"properties": [("tool_mode", "ALT")]}),
("kitfox.echo_tool", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("tool_mode", "SHIFT")]}),
("kitfox.echo_tool", {"type": 'LEFTMOUSE', "value": 'PRESS', "oskey": True},
{"properties": [("tool_mode", "OSKEY")]}),
("kitfox.echo_tool", {"type": 'LEFTMOUSE', "value": 'PRESS', "oskey" : True , "alt": True},
{"properties": [("tool_mode", "OS+ALT")]}),
("kitfox.echo_tool", {"type": 'MOUSEMOVE', "value": 'ANY' }, {"properties": []}),
)
class EchoToolOperator(bpy.types.Operator):
"""Echo tool"""
bl_idname = "kitfox.echo_tool"
bl_label = "Echo tool"
bl_options = {"REGISTER", "UNDO"}
is_running = False
tool_mode : bpy.props.StringProperty(
name="Tool Mode",
description="Tool Mode",
)
# @classmethod
# def poll( cls , context ) :
# return not EchoToolOperator.is_running
def __init__(self):
self.picking = False
def modal(self, context, event):
print("modal evTyp:%s evVal:%s mode:%s" % (str(event.type), str(event.value), self.tool_mode))
if context.mode != 'OBJECT':
EchoToolOperator.is_running = False
return {'CANCELED'}
return {'PASS_THROUGH'}
def invoke(self, context, event):
print("invoke evTyp:%s evVal:%s mode:%s" % (str(event.type), str(event.value), self.tool_mode))
EchoToolOperator.is_running = True
return {'RUNNING_MODAL'}