11
$\begingroup$

I would like to amend functionality of an existing operator with a condition check and then "proceed" with the original operator (if condition check allows it).
It is essentially what is done in answer to this question, however I would like to avoid writing the operator's code from scratch (as that answer does in main() ).

I know I can write a new operator and call the original from there but I don't want to add another operator, I want to replace (override) the original one.

So, is there a way to get to the "base" operator's methods from the override operator?

(In that example I managed to get the "OK?" prompt by adding invoke() and calling context.window_manager.popup_menu(). So that sort of solves my initial need, however, some operators are too complex to be rewritten from scratch and the need for calling the "base" operator still stands.)

$\endgroup$
3
  • $\begingroup$ Writing new operators code from scratch looks like it would work, then call the proceed to another operator in execute. Why don't you want to write a new operator from scratch? Can you do a OOP way and inherit the class from the parent (the operator you want to modify) and then modify/ register your child class from that ? $\endgroup$
    – 4nof
    Commented Jun 25, 2020 at 0:16
  • $\begingroup$ Well, as I wrote in the question 4 years ago, some operators are too complex to be rewritten from scratch. Besides it has been so many projects ago that I honestly do not remember the details any more. Thanks for the down-wote, @4nof. $\endgroup$
    – spacer
    Commented Jun 25, 2020 at 9:18
  • $\begingroup$ What's "the override operator" if you "don't want to add another operator"? Can you provide a "too complex operator to be rewritten from scratch"? Why can't you add your condition check inside the operator's execute or invoke if you "want to replace (override) the original one"? $\endgroup$
    – unwave
    Commented Oct 16, 2020 at 19:32

1 Answer 1

2
$\begingroup$

It is possible to run the original operator in an override by temporarily unregistering the overriding class during invoke() and execute() and that way avoiding infinite recursion (at least in Blender 2.93). Combining that with a technique for forwarding properties from this forum post https://blenderartists.org/t/extending-built-in-operator/1257955/4 allows you to write a forwarding operator.

def keywords_from_prop(p_rna):
    kw = {}
    for kwname in ("name", "description", "default", "min", "size"
                   "max", "soft_min", "soft_max", "precision"):
        value = getattr(p_rna, kwname, None)
        if value is not None:
            kw[kwname] = value
    if p_rna.type == 'ENUM':
        kw["items"] = tuple((i.identifier, i.name, i.description)
                            for i in p_rna.enum_items)
    return kw

def props_from_op(idname):
    import _bpy
    props_dict = {}
    op_rna = _bpy.ops.get_rna_type(idname)
    for p in op_rna.properties:
        if p.identifier == "rna_type":
            continue
        prop = getattr(bpy.props, p.rna_type.identifier)
        kwargs = keywords_from_prop(p)
        props_dict[p.identifier] = prop(**kwargs)
    return props_dict

class NewOperator(bpy.types.Operator):
    bl_idname = "object.delete" # override delete operator and copy all its attributes
    bl_label = bpy.ops.object.delete.get_rna_type().description
    # if we have our own undo steps, the REDO panel isn't usable
    #bl_options = {'UNDO','REGISTER'}
    
    __annotations__ = props_from_op("object.delete")
    
    @classmethod
    def poll(cls, context):
        return True # you will have to write your own poll method
    
    def execute(self, context):
        # unregister self before calling original operator to avoid infinite recursion
        bpy.utils.unregister_class(NewOperator)
        # execute operator: use True as 3rd parametre to register undo step
        ret = bpy.ops.sequencer.split({}, 'INVOKE_DEFAULT', True, **self.as_keywords())
        bpy.utils.register_class(NewOperator)
        return ret

Note that it could be hard to get the redo panel to work with this technique. In this example I am registering the undo step with the original operator. This way the redo panel keeps working but redoing will NOT trigger your operator again, only the original one. Operators with a modal phase or ones that adjust properties in the invoke phase are also harder to use in this way.

$\endgroup$
1
  • $\begingroup$ It was very useful, worked perfectly and solved my recursive problems. Thank you so much :) $\endgroup$
    – Ommadawn
    Commented May 7, 2023 at 10:53

You must log in to answer this question.

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