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.
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