# Run modal operator while tool is selected

For my addon in blender 2.8 I need to have a modal operator running while my custom tool in the new toolbar is selected, or at least know when it is selected/deselected so that I can invoke/stop a modal operator. I have not been able to find any way to do both these things. Is it possible with the current python API?

• Would you mind post some code for how you start the operator and where you add the button to the panel? May 7 '19 at 10:35

This is a hack--but for now (in 2.80/2.81) you can find out when a custom tool is activated and deactivated with a custom gizmo group:

class MYADDONNAME_TOOL_mytool(bpy.types.WorkSpaceTool):
bl_space_type='VIEW_3D'
bl_context_mode='OBJECT'
bl_label = "My tool"
bl_icon = "ops.transform.vertex_random"

bl_label = "(internal)"
bl_space_type = 'VIEW_3D'
bl_region_type = 'WINDOW'
bl_options = {'3D'}

@classmethod
def poll(cls, context):
return True

def setup(self, context):
print("My tool activated!")

def __del__(self):
print("My tool deactivated!")



This isn't enough to invoke a modal operator as you're asking for, because the setup() method gets called in a drawing/rendering context, not one where you can just call bpy.ops.whatever('INVOKE_DEFAULT'). But this might be a useful starting point.

• Thanks! This is exactly how I ended up doing it. I was able to solve the setup context problem by using bpy.app.timers.register to call a function which actually calls the modal operator. This is of course all very hackish, I hope there will be better options in the future :) Nov 24 '19 at 12:20
• Agree about the hackishness! In my addon I’m also using the GizmoGroup's draw_prepare() method to draw additional stuff, and my tool’s draw_cursor() method to get the mouse position and tag the area for a redraw… hacks upon hacks right now :-/ Nov 25 '19 at 1:44

I was also finding a similar problem on it. But I figured out an strange way to achieve it by using BoolProperty.

Here is the sample code:

import bpy
from bpy.props import (PointerProperty, BoolProperty)
from bpy.utils import register_class
from bpy.utils import unregister_class

class MyCustomProperties(bpy.types.PropertyGroup):
is_my_modal_run: BoolProperty(default=False)
# Use an bool to save modal status

class SOME_OT_myops(bpy.types.Operator):
bl_idname = "some.myops"
bl_label = "Some OP"

def execute(self, context):
self.report({"INFO"}, "Stop!")
return {'FINISHED'}

def modal(self, context, event):
# check bool status
if not context.scene.someprop.is_my_modal_run:
return self.execute(context)
return {'PASS_THROUGH'}

def invoke(self, context, event):
context.scene.someprop.is_my_modal_run = True
# When started, mark the bool flag
return {'RUNNING_MODAL'}

class SOME_PT_panel(bpy.types.Panel):
"""Landmark Panel"""
bl_idname = "SOME_PT_panel"
bl_label = "Some Panel"
bl_category = "Something"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"

def draw(self, context):
layout = self.layout
prop = context.scene.someprop

# Make the UI button change depend on the bool flag
if not prop.is_my_modal_run:
layout.operator(SOME_OT_myops.bl_idname,\
text='Start Running Modal')
else:
layout.prop(prop, 'is_my_modal_run',\
text="Stop Running Modal",toggle = True)

clss = [MyCustomProperties,SOME_OT_myops,SOME_PT_panel]

def register():
for cl in clss:
register_class(cl)
bpy.types.Scene.someprop = bpy.props.PointerProperty(\
type = MyCustomProperties)

def unregister():
for cl in clss[::-1]:
unregister_class(cl)
del bpy.types.Scene.someprop

if __name__ == "__main__":
register()



There MUST BE a better way, but I end up with this solution working.

• Thanks for the answer. However in my case I'm not using a button in a panel but rather a button in the new blender 2.8 toolbar on the left, so a similiar solution unfortunately doesn't work. (I need to know when the toolbar button is deselected) May 4 '19 at 11:24