How to access and draw the node group input slider in layout panel

How can I access the node group and then draw the input sliders of node group in layout panel my test script is this

bl_info = {
    "name": "Add Test Material",
    "author": "Rakesh Choudhary",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > Sidebar > Test Material Node",
    "description": "Click on the 'Test Material' button to add a material to your object.",
    "warning": "",
    "wiki_url": "",
    "category": "3D View"
}

import bpy


class TestMaterial_OT_add_material(bpy.types.Operator):
    bl_idname = "test_material.add_material"
    bl_label = "Add Test Material"
    bl_description = "This button will add a material to your object"

    def execute(self, context):
        self.create_material()
        return {'FINISHED'}

    def create_material(self):
        bpy.context.scene.use_nodes = True
        tree = bpy.context.scene.node_tree

        # removes unwanted nodes
        for node in tree.nodes:
            tree.nodes.remove(node)

        test_shader_mat = bpy.data.materials.new("TestMat")
        mesh = bpy.context.object.data
        mesh.materials.clear()
        mesh.materials.append(test_shader_mat)
        bpy.context.object.active_material.use_nodes = True

        for mat in bpy.data.materials:
            if "TestMat" in mat.name:
                nodes = mat.node_tree.nodes
                for node in nodes:
                    if node.type != 'OUTPUT_MATERIAL':  # skip the material output node as we'll need it later
                        nodes.remove(node)

        # Creating Node Group Test_Material
        group = bpy.data.node_groups.new(type="ShaderNodeTree", name="Test_Material")

        # Creating Group Input
        group.inputs.new("NodeSocketColor", "Diffuse Color")
        group.inputs.new("NodeSocketColor", "Glossy Color")
        group.inputs.new("NodeSocketFloat", "Mix Factor")
        group.inputs.new("NodeSocketFloat", "Glossyness")
        input_node = group.nodes.new("NodeGroupInput")
        input_node.location = (-800, 0)

        # Creating Group Output Node
        group.outputs.new("NodeSocketShader", "Diffuse Color")
        group.outputs.new("NodeSocketShader", "Glossy Color")
        group.outputs.new("NodeSocketShader", "Mix Output")

        output_node = group.nodes.new("NodeGroupOutput")
        output_node.location = (1500, 0)

        # Creating Diffuse Node
        diffuse_node = group.nodes.new(type='ShaderNodeBsdfDiffuse')
        diffuse_node.location = (150, 100)

        # Creating Glossy Node
        glossy_node = group.nodes.new(type='ShaderNodeBsdfGlossy')
        glossy_node.location = (300, 250)

        # Creating Mix Shader Node
        mix_shader_node = group.nodes.new(type='ShaderNodeMixShader')
        mix_shader_node.location = (450, 100)

        # Creating Links Between Nodes
        group.links.new(diffuse_node.outputs["BSDF"], mix_shader_node.inputs[1])
        group.links.new(glossy_node.outputs["BSDF"], mix_shader_node.inputs[2])
        group.links.new(input_node.outputs["Diffuse Color"], diffuse_node.inputs[0])
        group.links.new(input_node.outputs["Glossy Color"], glossy_node.inputs[0])
        group.links.new(input_node.outputs["Mix Factor"], mix_shader_node.inputs[0])
        group.links.new(input_node.outputs["Glossyness"], glossy_node.inputs[1])
        group.links.new(output_node.inputs["Diffuse Color"], diffuse_node.outputs[0])
        group.links.new(output_node.inputs["Glossy Color"], glossy_node.outputs[0])
        group.links.new(output_node.inputs["Mix Output"], mix_shader_node.outputs[0])

        # Putting Node Group to the node editor
        tree = bpy.context.object.active_material.node_tree
        group_node = tree.nodes.new("ShaderNodeGroup")
        group_node.node_tree = group
        group_node.location = (-40, 300)
        group_node.use_custom_color = True
        group_node.color = (1, 0.341, 0.034)
        group_node.width = 250

        shader_node_output_material_node = tree.nodes["Material Output"]
        links = tree.links
        links.new(group_node.outputs[0], shader_node_output_material_node.inputs[0])


class TestMaterial_PT_layout_panel(bpy.types.Panel):
    bl_label = "Test Material Node"
    bl_category = "Test Material"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        layout.operator("test_material.add_material", icon='IMPORT')


classes = (TestMaterial_OT_add_material, TestMaterial_PT_layout_panel)


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


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


if __name__ == "__main__":
    register()

Node groups are stored in bpy.data.node_groups… you can access their trees through there.

You’ll need a reference to the nodegroup instance, and with it, you can easilty draw some property wherever you want:

# reference to the nodegroup instance
    node_ref = bpy.data.materials['TestMat'].node_tree.nodes['Group']
# replacement for the draw function of your panel:
    def draw(self, context):
        layout = self.layout
        layout.operator("test_material.add_material", icon='IMPORT')
        layout.prop(node_ref.inputs['Mix Factor'], 'default_value', text="Mix Factor")
1 Like

I have added this code to the script but it is not working throwing error -
KeyError: ‘bpy_prop_collection[key]: key “TestMat” not found’
Error: Python script failed, check the message in the system console.

Sorry as i am new to python may be i have made a mistake

Should i create a node group instance or directly add this code to the script.
Please help me out

This is My script after change -

bl_info = {
    "name": "Add Test Material",
    "author": "Rakesh Choudhary",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > Sidebar > Test Material Node",
    "description": "Click on the 'Test Material' button to add a material to your object.",
    "warning": "",
    "wiki_url": "",
    "category": "3D View"
}

import bpy


class TestMaterial_OT_add_material(bpy.types.Operator):
    bl_idname = "test_material.add_material"
    bl_label = "Add Test Material"
    bl_description = "This button will add a material to your object"

    def execute(self, context):
        self.create_material()
        return {'FINISHED'}

    def create_material(self):
        bpy.context.scene.use_nodes = True
        tree = bpy.context.scene.node_tree

        # removes unwanted nodes
        for node in tree.nodes:
            tree.nodes.remove(node)

        test_shader_mat = bpy.data.materials.new("TestMat")
        mesh = bpy.context.object.data
        mesh.materials.clear()
        mesh.materials.append(test_shader_mat)
        bpy.context.object.active_material.use_nodes = True

        for mat in bpy.data.materials:
            if "TestMat" in mat.name:
                nodes = mat.node_tree.nodes
                for node in nodes:
                    if node.type != 'OUTPUT_MATERIAL':  # skip the material output node as we'll need it later
                        nodes.remove(node)

        # Creating Node Group Test_Material
        group = bpy.data.node_groups.new(type="ShaderNodeTree", name="Test_Material")

        # Creating Group Input
        group.inputs.new("NodeSocketColor", "Diffuse Color")
        group.inputs.new("NodeSocketColor", "Glossy Color")
        group.inputs.new("NodeSocketFloat", "Mix Factor")
        group.inputs.new("NodeSocketFloat", "Glossyness")
        input_node = group.nodes.new("NodeGroupInput")
        input_node.location = (-800, 0)

        # Creating Group Output Node
        group.outputs.new("NodeSocketShader", "Diffuse Color")
        group.outputs.new("NodeSocketShader", "Glossy Color")
        group.outputs.new("NodeSocketShader", "Mix Output")

        output_node = group.nodes.new("NodeGroupOutput")
        output_node.location = (1500, 0)

        # Creating Diffuse Node
        diffuse_node = group.nodes.new(type='ShaderNodeBsdfDiffuse')
        diffuse_node.location = (150, 100)

        # Creating Glossy Node
        glossy_node = group.nodes.new(type='ShaderNodeBsdfGlossy')
        glossy_node.location = (300, 250)

        # Creating Mix Shader Node
        mix_shader_node = group.nodes.new(type='ShaderNodeMixShader')
        mix_shader_node.location = (450, 100)

        # Creating Links Between Nodes
        group.links.new(diffuse_node.outputs["BSDF"], mix_shader_node.inputs[1])
        group.links.new(glossy_node.outputs["BSDF"], mix_shader_node.inputs[2])
        group.links.new(input_node.outputs["Diffuse Color"], diffuse_node.inputs[0])
        group.links.new(input_node.outputs["Glossy Color"], glossy_node.inputs[0])
        group.links.new(input_node.outputs["Mix Factor"], mix_shader_node.inputs[0])
        group.links.new(input_node.outputs["Glossyness"], glossy_node.inputs[1])
        group.links.new(output_node.inputs["Diffuse Color"], diffuse_node.outputs[0])
        group.links.new(output_node.inputs["Glossy Color"], glossy_node.outputs[0])
        group.links.new(output_node.inputs["Mix Output"], mix_shader_node.outputs[0])

        # Putting Node Group to the node editor
        tree = bpy.context.object.active_material.node_tree
        group_node = tree.nodes.new("ShaderNodeGroup")
        group_node.node_tree = group
        group_node.location = (-40, 300)
        group_node.use_custom_color = True
        group_node.color = (1, 0.341, 0.034)
        group_node.width = 250

        shader_node_output_material_node = tree.nodes["Material Output"]
        links = tree.links
        links.new(group_node.outputs[0], shader_node_output_material_node.inputs[0])


class TestMaterial_PT_layout_panel(bpy.types.Panel):
    bl_label = "Test Material Node"
    bl_category = "Test Material"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
# reference to the nodegroup instance
    node_ref = bpy.data.materials['TestMat'].node_tree.nodes['Group']
    
# replacement for the draw function of your panel:
    def draw(self, context):
        layout = self.layout
        layout.operator("test_material.add_material", icon='IMPORT')
        layout.prop(node_ref.inputs['Mix Factor'], 'default_value', text="Mix Factor")


classes = (TestMaterial_OT_add_material, TestMaterial_PT_layout_panel)


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


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


if __name__ == "__main__":
    register()

This means that you don’t have a material with the name ‘TestMat’ to assign to the node_ref variable.

My previous answer only points directly to the question you made: ‘Draw the input sliders of the node group’… If there’s no nodegroup, there’s nothing to draw.
So you’ll need to create code to verify if everything is correct before using it, starting by writing poll functions for your classes, and checking if every variable is correct before you use them.

As You can see here -

 test_shader_mat = bpy.data.materials.new("TestMat") 
mesh = bpy.context.object.data mesh.materials.clear() 
mesh.materials.append(test_shader_mat) bpy.context.object.active_material.use_nodes = True

The Material with the Name TestMat is Created in Script
but i don’t understand why it is throwing error.

The material is created, but only when you call the operator… In the meanwhile, the panel is called before the operator and that’s the error.

Then what is the solution what should I do Now to Solve this

Typical programming tip: Allways check if your variables are correct before using them…
In this case, you’ll need to check if the material exist and only draw the layout.prop if it does.

I started to post a solution ,but one solution is in your operator class. I haven’t seen any code that you’ve done yourself, so I’m not going to post my solution.

1 Like

As i am Not a Professional Python scripter so I don’t Know Much about it
But Earlier this script was written by me and it was Having some errors so I posted this on stackexchange and somebody Gave me the solution and finally the script was fixed in that context and now i have this new problem so i posted here as I am learning by posting questions and i have actually learnt many things so don’t be rude as basically i wrote this script and i was wandering for the fixes

Yeah ,but nothing is different than your original script you posted like 4-5 threads ago. The only changes is what other people have posted.

import bpy


def confirm_testmat_group():
    #reference to the nodegroup instance
    for mat in bpy.data.materials:
        #This gets only 'TestMat' material 
        if mat.name == 'TestMat':
            node_ref = mat.node_tree.nodes['Group']
            return node_ref


class TestMaterial_PT_layout_panel(bpy.types.Panel):
    bl_label = "Test Material Node"
    bl_category = "Test Material"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    #replacement for the draw function of your panel:
    def draw(self, context):
        layout = self.layout
        #layout.operator("test_material.add_material", icon='IMPORT')

        #if node group found, draw all input properties                                
        if confirm_testmat_group():
            node_ref = confirm_testmat_group()            
            for i in node_ref.inputs:        
                layout.prop(i, 'default_value', text=i.name)


classes = (TestMaterial_PT_layout_panel,)


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


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


if __name__ == "__main__":
    register()
1 Like

I am Very Very Glad that You finally gave the solution
Thanks !

But still there is one problem when i hit the button again then it shows an error it creates material but did not draw any input properties in Layout Panel