5
$\begingroup$

I have successfully created a sub menu which extends the Shift+A menu in the shader editor but I would like to have the options show up as results in the search bar. Is this even possible, and if so how can it be done?

enter image description here

import bpy
from bpy.types import Menu, Operator
 
def draw_menu(self, context):
    layout = self.layout
    layout.separator()
    layout.menu("sub_menu")
    
class SubMenu(bpy.types.Menu):
    bl_label = 'Math'
    bl_idname = 'sub_menu'
 
    def draw(self, context):
        layout = self.layout
        layout.operator("divide.node", text="Divide")
        layout.operator("multiply.node", text="Multiply")
        layout.operator("add.node", text="Add")
        layout.operator("subtract.node", text="Subtract")
 
class DivideNode(Operator):
    bl_idname = "divide.node"
    bl_label = "DivideNode"
 
    def execute(self, context):
        bpy.ops.node.add_node(type="ShaderNodeMath", use_transform=True)
        bpy.data.materials["Material"].node_tree.nodes["Math"].operation = 'DIVIDE'
 
        return {'FINISHED'}    
 
class AddNode(Operator):
    bl_idname = "add.node"
    bl_label = "AddNode"
 
    def execute(self, context):
        bpy.ops.node.add_node(type="ShaderNodeMath", use_transform=True)
        bpy.data.materials["Material"].node_tree.nodes["Math"].operation = 'ADD'
 
        return {'FINISHED'}   
      
class SubtractNode(Operator):
    bl_idname = "subtract.node"
    bl_label = "SubtractNode"
 
    def execute(self, context):
        bpy.ops.node.add_node(type="ShaderNodeMath", use_transform=True)
        bpy.data.materials["Material"].node_tree.nodes["Math"].operation = 'SUBTRACT'
 
        return {'FINISHED'}   
       
class MultiplyNode(Operator):
    bl_idname = "multiply.node"
    bl_label = "MultiplyNode"
 
    def execute(self, context):
        bpy.ops.node.add_node(type="ShaderNodeMath", use_transform=True)
        bpy.data.materials["Material"].node_tree.nodes["Math"].operation = 'MULTIPLY'
 
        return {'FINISHED'}    
 
def register():
    bpy.types.NODE_MT_add.append(draw_menu)
    bpy.utils.register_class(MultiplyNode)
    bpy.utils.register_class(SubMenu)
    bpy.utils.register_class(DivideNode)
    bpy.utils.register_class(SubtractNode)
    bpy.utils.register_class(AddNode)
 
 
def unregister():
    bpy.types.NODE_MT_add.remove(draw_menu)
    bpy.utils.unregister_class(MultiplyNode)
    bpy.utils.unregister_class(SubMenu)
    bpy.utils.unregister_class(DivideNode)
    bpy.utils.unregister_class(SubtractNode)
    bpy.utils.unregister_class(AddNode)
 
if __name__ == "__main__":
    register()
 
 
$\endgroup$
2

1 Answer 1

7
$\begingroup$

All the math node operations

Note: this doesn't answer how to put your operators into the search, rather is intended to offer another way to have same functionality in a far more easily extendable way

enter image description here

From one look at the question code instantly has me quoting

Foghorn Leghorn

No, no, no your're doing it all wrong

No menus one operator

Have taken code above and edited to have functionality as demonstrated, with no menus and one operator.

All the question operators are doing pretty much the same thing, the code varies little between them. Instead of an operator for each operation, one operator and an enum of possible operations.

The tricky bit.

When we add a math node, we add an instance of the type bpy.types.ShaderNodeMath. All the possible operations are available from its definition of the "operation" property.

[Look for links, been well covered]

Our one operator is going to pinch these.

The menus can be ditched because blender can automatically make a menu from the options. And boy there are a few. The items list can be narrowed to a select few if desired

Categorize the other way

The naming convention of your operators is, going by "standards", a bit A about T. Using "node.add_math_subtract" will classify it with all the other bpy.ops.node prefixed operators.

Operators use context

For any code run in the shader editor the material we're "lookin' at" is context material. This is the material the bpy.ops.node.foo_bar will work on. This will not always be bpy.data.materials["Material"]

Similarly only the first math node added via operator (bpy.ops.node.add_node(..)) will have the name "Math". Hence on subsequent runnings of question code will add a new node but change the operation of the first one added, ie the one with name "Math". Directly after running the operator the new node will be context.active_node .. another context member of the node editor.

Have instead added a math node to the node tree of the context material and set its operation and name accordingly.

import bpy
from bpy.types import Menu, Operator, ShaderNodeMath
from bpy.props import EnumProperty
 
class AddMathNode(Operator):
    ''' Add Math Node '''
    bl_idname = "node.add_math"
    bl_label = "Add Math Node"
    items = [
        (p.identifier, p.name, p.description) 
        for p in ShaderNodeMath.bl_rna.properties["operation"].enum_items
            ]
    #print(items)
    operation : EnumProperty(
        items=items,
        name="Operation",
        default='ADD',
        )
            
    def execute(self, context):
        # the material in the node editor
        mat = context.material  
        # add a new math node to its nodes         
        n = mat.node_tree.nodes.new("ShaderNodeMath")
        # set some properties
        n.operation = self.operation
        n.name = self.operation.lower().title()
        # make active
        mat.node_tree.nodes.active = n
        return {'FINISHED'}
        #return bpy.ops.transform.translate('INVOKE_DEFAULT') # use translate   
      
classes = (AddMathNode,)
def draw_menu(self, context):
    layout = self.layout
    layout.operator_menu_enum("node.add_math", "operation")
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.NODE_MT_add.append(draw_menu)
 
def unregister():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.NODE_MT_add.remove(draw_menu)

 
if __name__ == "__main__":
    register()

Sorting them

There's lots of them. This change circa lines 9-12 sorts them by IDENTIFIER, eg ADD, SUBTRACT

    items = sorted(
        (p.identifier, p.name, p.description) 
        for p in ShaderNodeMath.bl_rna.properties["operation"].enum_items
            )

Adding to the search menu.

Because this is now only a single operator removes the sense of searching for it. Possibly the logical extension of this is to categorize the operation (as is done via the UI to select math node operation)

With developer extras set to show view source option can right click over the add nodes menu and view source, revealing how the search menu is constructed and laid out.

def draw(self, context):
    layout = self.layout

    layout.operator_context = 'INVOKE_DEFAULT'

    if nodeitems_utils.has_node_categories(context):
        props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
        props.use_transform = True

        layout.separator()

        # actual node submenus are defined by draw functions from node categories
        nodeitems_utils.draw_node_categories_menu(self, context)

Which reveals a helper module nodeitems_utils which we can pretty much pinch, to instead of using enum menu expansion can search and categorize. Perhaps for another day.

$\endgroup$
1

You must log in to answer this question.

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