2
$\begingroup$

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?

$\endgroup$
1
  • $\begingroup$ Would you mind post some code for how you start the operator and where you add the button to the panel? $\endgroup$
    – HikariTW
    May 7, 2019 at 10:35

2 Answers 2

4
$\begingroup$

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.

$\endgroup$
2
  • $\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$
    – yggdrasil
    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$
    – adurdin
    Nov 25, 2019 at 1:44
3
$\begingroup$

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.

$\endgroup$
1
  • 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$
    – yggdrasil
    May 4, 2019 at 11:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .