4
$\begingroup$

I want to make a toggle button for my custom operator. It works similar to checkbox: once the button is clicked, the operator keeps running until the button gets another click, so will the button itself greyed out. I tried to achieve the grey-out effect by implementing poll() function like this:

@classmethod
def poll(cls, context):      
    return context.area.type == 'VIEW_3D' and context.region.type == 'WINDOW' and plane_toggle_flag

Every time the button is clicked I want the toggle state to change, so in the operator's init(), I write:

def __init__(self):   
    plane_toggle_flag=not plane_toggle_flag

However, it seems as long as poll() returns false, the init() function never get the chance to be called. Any idea how to solve this? Thank you in advance.

$\endgroup$
7
$\begingroup$
  1. Create a bool property for Window Manager (it can be for anything, but WM will store our modal operator handler so why not put this in the same place):

    bpy.types.WindowManager.my_operator_toggle = bpy.props.BoolProperty()
    
  2. Display it somewhere in the UI with the toggle tag on:

    layout = self.layout
    wm = context.window_manager
    label = "Operator ON" if wm.my_operator_toggle else "Operator OFF"
    layout.prop(wm, 'my_operator_toggle', text=label, toggle=True)
    

    It looks like this:

    enter image description here

  3. Create your modal operator (example of basic timer):

    class SomeModalOperator(bpy.types.Operator):
        bl_idname = "my.operator"
        bl_label = "My Operator"
    
        def modal(self, context, event):
            print("running")
            if not context.window_manager.my_operator_toggle:
                context.window_manager.event_timer_remove(self._timer)
                return {'FINISHED'}
            return {'PASS_THROUGH'}
    
        def invoke(self, context, event):
            self._timer = context.window_manager.event_timer_add(0.01, context.window)
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
    

    The operator ends itself when the bool property is False.

  4. Add update function to the bool property that will run the operator:

    def update_function(self, context):
        if self.my_operator_toggle:
            bpy.ops.my.operator('INVOKE_DEFAULT')
        return
    
    bpy.types.WindowManager.my_operator_toggle = bpy.props.BoolProperty(
                                                     default = False,
                                                     update = update_function)
    

The modal operator is useful when you need to handle user input. For anything else it is better to use app.handlers, you can toggle them through bool property in similar fashion.

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.