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?
2 Answers
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_idname = "myaddonname.mytool"
bl_space_type='VIEW_3D'
bl_context_mode='OBJECT'
bl_label = "My tool"
bl_icon = "ops.transform.vertex_random"
bl_widget = "MYADDONNAME_GGT_mytool_activated"
class MYADDONNAME_GGT_mytool_activated(bpy.types.GizmoGroup):
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!")
bpy.utils.register_class(MYADDONNAME_GGT_mytool_activated)
bpy.utils.register_tool(MYADDONNAME_TOOL_mytool, separator=True)
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.
-
$\begingroup$ 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 :) $\endgroup$ Nov 24, 2019 at 12:20
-
$\begingroup$ 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 :-/ $\endgroup$– adurdinNov 25, 2019 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.window_manager.modal_handler_add(self)
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.
-
1$\begingroup$ 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) $\endgroup$ May 4, 2019 at 11:24