Boolean check if an operator is being used (bpy.ops.anim.previewrange_set)?

Hello, I’m struggling to check if the operator bpy.ops.anim.previewrange_set is in effect in the Video Sequencer.
I’m trying to make myself a custom addon where I want to merge the 2 following operators into one button:
bpy.ops.anim.previewrange_set
bpy.ops.anim.previewrange_clear

The idea is that if there is no preview range, the button is normal and I can click to make one.
If there is a preview range, the button is red (alert) and I can click it so clears the preview range.

My code so far:

if bpy.ops.anim.previewrange_set != None: 
    row.alert = True
    row.operator("anim.previewrange_clear", text="", icon="BORDERMOVE")
elif bpy.ops.anim.previewrange_clear != None:
    row.operator("anim.previewrange_set", text="", icon="BORDERMOVE")

Any ideas?
Thanks.

operators are fire-and-forget, you can’t query them. bpy.ops.anim.previewrange_set will never be “None” because it will resolve to a pointer to the built-in function.

1 Like

I once made something similar for my Cameras Lister addon, but I’m unable to replicate it for this specific operator…

Here’s what worked (for the Cameras Lister):

if ((context.area.spaces[0].region_3d.view_perspective == 'PERSP' or context.area.spaces[0].region_3d.view_perspective == 'ORTHO')
    and context.area.spaces.active.use_render_border == False):
        row.operator("view3d.render_border", text="", icon="BORDERMOVE")
    elif ((context.area.spaces[0].region_3d.view_perspective == 'PERSP' or context.area.spaces[0].region_3d.view_perspective == 'ORTHO')
    and context.area.spaces.active.use_render_border == True):
        row.alert = True
        row.operator("view3d.clear_render_border", text="", icon="BORDERMOVE")
    if context.area.spaces[0].region_3d.view_perspective == 'CAMERA' and bpy.data.scenes["Scene"].render.use_border == False:
        row.operator("view3d.render_border", text="", icon="BORDERMOVE")

Here’s a screen capture:
GIF

Is there a way I can do it with something similar to the code above?

Here is how you can do it

import bpy

# operator that displays the menu
class ShowMenu(bpy.types.Operator):
    bl_idname = "object.show_menu"
    bl_label = "Show Menu"
    def execute(self, context):
        return bpy.ops.wm.call_menu(name=CustomMenu.bl_idname)

# menu with UI
class CustomMenu(bpy.types.Menu):
    bl_label = "Custom Menu"
    bl_idname = "OBJECT_MT_custom_menu"
    def draw(self, context):
        layout = self.layout
        # get all spaces
        for space in context.area.spaces:
            # get only view3D
            if space.type != 'VIEW_3D': continue;
            # if it has no render border -> exec operator with renderborder
            if not space.use_render_border:
                layout.operator('object.exec_operator', text='draw border').action='renderborder'
                return
            # it has render border -> exec operator with 'clearborder'
            else:
                layout.operator('object.exec_operator', text='clear border').action='clearborder'

# is only a wrapper operator that will call the real operators
class ExecOperator(bpy.types.Operator):
    bl_idname = "object.exec_operator"
    bl_label = "Show Menu"
    bl_options = {'INTERNAL'} # this operator is hidden from user
    action : bpy.props.StringProperty()
    def execute(self, context):
        # can execute only two actions
        if self.action == 'renderborder':
            # INVOKE_DEFAULT is used to override BLENDER's default context
            # --> now blender will be confused and assume
            #     that the user wants to call the operator
            #     instead of how by default Blender/Operator calls the oper
            bpy.ops.view3d.render_border('INVOKE_DEFAULT')
        elif self.action == 'clearborder':
            # since this does not involve user-level-interaction
            # INVOKE_DEFAULT is not needed essentially
            # (either you use it or not makes not so much of a differnce)
            bpy.ops.view3d.clear_render_border()
        return {'FINISHED'}

def register():
    bpy.utils.register_class(ShowMenu) # to begin display the menu
    bpy.utils.register_class(CustomMenu) # to draw the menu
    bpy.utils.register_class(ExecOperator) # to execute the operators (CustomMenu->ExecOperator)

def unregister():
    bpy.utils.register_class(ShowMenu)  
    bpy.utils.unregister_class(CustomMenu)
    bpy.utils.register_class(ExecOperator)    

# while testing unregister/register all of the time
# (first time ever will fail for sure, so exception is used)
try: unregister()
except: pass
register()
1 Like

Sorry for the late answer.

So I tried your script and changed a few things as I wanted this to be in the Sequence Editor and for the anim.previewrange_set and anim.previewrange_clear operators.

I changed, in class CustomMenu:
if space.type != 'SEQUENCE_EDITOR': continue;

and in class ExecOperator:
bpy.ops.anim.previewrange_set('INVOKE_DEFAULT')
bpy.ops.anim.previewrange_clear()

But it’s that line here that is causing me a problem:
if not space.use_render_border:

If I check the API, I don’t get how I can use anim.previewrange_set in an ‘if’ statement…
https://docs.blender.org/api/current/search.html?q=previewrange&check_keywords=yes&area=default#

you can’t

What @const is suggesting is that you find another way to solve your problem- you are solving for X, when your problem is Y.

1 Like

I understand that I can’t do it directly, but with Const’s solution I can’t adapt that line for my purpose:

for space in context.area.spaces:
	if space.type != 'SEQUENCE_EDITOR': continue;
		if not space.anim.previewrange_set:

because anim.previewrange_set is not taken into account for that editor “space” and I get the following error:

You can mix both space types and both operator calls:

import bpy

class ShowMenu(bpy.types.Operator):
    bl_idname = "object.show_menu"
    bl_label = "Show Menu"
    def execute(self, context):
        return bpy.ops.wm.call_menu(name=CustomMenu.bl_idname)

class CustomMenu(bpy.types.Menu):
    bl_label = "Custom Menu"
    bl_idname = "OBJECT_MT_custom_menu"
    def draw(self, context):
        layout = self.layout
        
        for space in context.area.spaces:
            
            if space.type == 'VIEW_3D':    
                if not space.use_render_border:
                    layout.operator('object.exec_operator', text='draw border').action='renderborder.view3d'
                else:
                    layout.operator('object.exec_operator', text='clear border').action='clearborder.view3d'
                    
            elif space.type == 'SEQUENCE_EDITOR':
                haspreviewrange = True
                if haspreviewrange:
                    layout.operator('object.exec_operator', text='draw border').action='renderborder.sequence'
                else:
                    layout.operator('object.exec_operator', text='clear border').action='clearborder.sequence'
                

class ExecOperator(bpy.types.Operator):
    bl_idname = "object.exec_operator"
    bl_label = "Show Menu"
    bl_options = {'INTERNAL'}
    action : bpy.props.StringProperty()
    def execute(self, context):
        
        if self.action == 'renderborder.view3d':
            bpy.ops.view3d.render_border('INVOKE_DEFAULT')
        elif self.action == 'renderborder.view3d':
            bpy.ops.view3d.clear_render_border()
            
        elif self.action == 'renderborder.sequence':
            bpy.ops.anim.previewrange_set('INVOKE_DEFAULT')
        elif self.action == 'clearborder.sequence':
            bpy.ops.anim.previewrange_clear()
        
        return {'FINISHED'}

def register():
    bpy.utils.register_class(ShowMenu)
    bpy.utils.register_class(CustomMenu)
    bpy.utils.register_class(ExecOperator)

def unregister():
    bpy.utils.unregister_class(ShowMenu)  
    bpy.utils.unregister_class(CustomMenu)
    bpy.utils.unregister_class(ExecOperator)    

try: unregister()
except: pass
register()

However in sequence editor, how you can figure out if the preview range is on? If you can find it just add the change because now it only draws it.