0
$\begingroup$

Issue

enter image description here

Currently, node groups are only accessible in the Add menu then group sub-menu. You can't just click and drag them into use. You have to search, or go into sub-menus to use them.

Question

It would be great to be able to have these group nodes also exposed in the side panel with names dynamically, so it's easier to see what groups are available and to re-use them easily. How can I expose this enumerated list as a dynamic list of buttons in a panel in the shelf?

$\endgroup$
0

1 Answer 1

2
$\begingroup$

That's actually quite simple. bpy.data.node_groups returns a collection of all available node groups within the file. You just have to make sure that bl_idname is equal to ShaderNodeTree in this case:

>>> bpy.data.node_groups
<bpy_collection[10], BlendDataNodeTrees>

>>> [ng for ng in bpy.data.node_groups if ng.bl_idname == 'ShaderNodeTree']
[bpy.data.node_groups['My Node Group I'], 
bpy.data.node_groups['My Node Group II'], 
bpy.data.node_groups['My Node Group III']]

Further reading: How to get all types of nodes dynamically?


Based on that knowledge you would have to create a generic operator for a 'button' that adds a group node to the current node tree by passing the name of the node group. Declare a panel and iterate through the data collection in order to add the button per node group on the panel.

enter image description here

Demo code based on How to create a custom UI?

import bpy

# ------------------------------------------------------------------------
#    Operators
# ------------------------------------------------------------------------

class NODE_OT_add_group_node(bpy.types.Operator):
    bl_label = "Add Group Node by Name"
    bl_idname = "node.add_node_by_name"
    
    node_name : bpy.props.StringProperty()
    
    @classmethod
    def poll(cls, context):
        return context.space_data.tree_type == 'ShaderNodeTree'
    
    def execute(self, context):
        node_tree = context.space_data.node_tree
        node = node_tree.nodes.new(type='ShaderNodeGroup')
        node.node_tree = bpy.data.node_groups[self.node_name]
        return {'FINISHED'}

# ------------------------------------------------------------------------
#    Panel in Shader Editor
# ------------------------------------------------------------------------

class NODE_PT_CustomPanel(bpy.types.Panel):
    bl_label = "Node Groups"
    bl_idname = "NODE_PT_add_node_groups"
    bl_space_type = 'NODE_EDITOR'
    bl_label = "Node Groups"
    bl_region_type = "UI"
    bl_category = "Node Groups"
    
    @classmethod
    def poll(self,context):
        return context.space_data.tree_type == 'ShaderNodeTree'

    def draw(self, context):
                
        node_groups = [ng for ng in bpy.data.node_groups if ng.bl_idname == 'ShaderNodeTree']
        for ng in node_groups:
            self.layout.operator(NODE_OT_add_group_node.bl_idname, text=ng.name).node_name = ng.name
            
        self.layout.separator()

# ------------------------------------------------------------------------
#    Registration
# ------------------------------------------------------------------------

classes = (
    NODE_OT_add_group_node,
    NODE_PT_CustomPanel
)

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)


if __name__ == "__main__":
    register()

Also read: Control Cycles material nodes and material properties in Python

$\endgroup$

You must log in to answer this question.

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