# Accessing nodes in a node group to draw float property in panel

I would Like to access the position of the element of a color ramp and directly draw that in the panel as a Float Value Slider so whenever the value in that slider is changed it updates in the color ramp.

I have Created a color ramp with:

col_ramp = group.nodes.new(type="ShaderNodeValToRGB")


And then assigned a position to it:

col_ramp.color_ramp.elements.new(0.750)
col_ramp.color_ramp.elements[0].color = (0,0,0,1)


And I have defined and operator and that will only be executed when the button is pressed from the Panel, and What I don't know is how to access that color ramp element.

I have tried this:

position_control = context.Scene.node_tree.col_ramp.color_ramp.elements[0].position


And this:

position_control = bpy.data.materials["TestMat"].node_tree.nodes["col_ramp"].elements[0].position


But finally, this is giving an error in the console like this:

AttributeError: ramp_pos Error: Python script failed, check the message in the system console

And showing error in line:

del bpy.types.Scene.ramp_pos


Reference to the previously asked question by me relating this Drawing Float Property

class Col_ramp_Property(PropertyGroup):
ramp_pos : FloatProperty(
name = "Position",
description = "A float property",
default = 0.75,
min = 0.01,
max = 1.0
)

class TEST_MATERIAL_PT_layout_panel(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
position_control = context.Scene.active_object.active_material.node_tree.nodes.col_ramp.color_ramp.elements[0].position
row = layout.row()
row.prop(position_control, "ramp_pos")

def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.ramp_pos = PointerProperty(type=Col_ramp_Property)

def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
del bpy.types.Scene.ramp_pos

if __name__ == "__main__":
register()


Update: I tried doing as in the answer, but this is not working for me, when I tried running this script(written below), it gives this error

TypeError: must be real number, not str Traceback (most recent call last): File "\TEST_MATERIAL.py", line 181, in File "\TEST_MATERIAL.py", line 169, in register ValueError: bpy_struct "Col_ramp_Property" registration error: ramp_pos could not register

Error: Python script failed, check the message in the system console

for knowing the error I tried removing class Col_ramp_Property from classes and in the register and unregister also, and then I got this error -

Traceback (most recent call last): File "\TEST_MATERIAL.py", line 142, in draw AttributeError: 'Object' object has no attribute 'material'

location: :-1

location: :-1

I don't know what I did wrong, can you Please Show me the mistake I made in this -

bl_info = {
"author": "Rakesh Choudhary",
"version": (1, 0),
"blender": (2, 83, 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
from bpy.types import (
Operator,
Panel,
PropertyGroup,
)
from bpy.props import (
FloatProperty,
PointerProperty,
)

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

def create_material(self):
mesh = bpy.context.object.data
mesh.materials.clear()
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

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

# Creating Group Output Node

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

# Creating Diffuse Node
diffuse_node.location = (150, 100)

# Creating Glossy Node
glossy_node.location = (300, 250)

#Creating Color Ramp ------------------------------------------------------
col_ramp.name = "col_ramp"
col_ramp.location = (400, -300)
col_ramp.color_ramp.elements.remove(col_ramp.color_ramp.elements[0])

col_ramp.color_ramp.elements.new(0.750)
col_ramp.color_ramp.elements[0].color = (0,0,0,1)

col_ramp.color_ramp.elements[1].position = (1.0)
col_ramp.color_ramp.elements[1].color = (1, 1, 1, 1)

# Putting Node Group to the node editor
tree = bpy.context.object.active_material.node_tree
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

#Material ends here------------------------------

class Col_ramp_Property(PropertyGroup):
ramp_pos : FloatProperty(
name = "Position",
description = "A Float Property",
default = "0.750",
min = "0.010",
max = "1.00"
)

class TEST_MATERIAL_PT_layout_panel(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
active_object = context.view_layer.objects.active
if not active_object:
print("Select an Object")
return
active_material = active_object.material
if not active_material:
print("Add a material to the selected object")
return
node_tree = active_material.node_tree
if not node_tree:
print("Check 'Use Nodes' in the active material")
return
color_ramp = node_tree.nodes.get("col_ramp")
if not color_ramp_node:
print("Add a color ramp node to the active material")
return

position_control = color_ramp.color_ramp.elements[0]

row = layout.row()
row.prop(position_control, "ramp_pos")

def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.ramp_pos = PointerProperty(type=Col_ramp_Property)

def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
del bpy.types.Scene.ramp_pos

if __name__ == "__main__":
register()

• Hello, if you don't actually rename a color ramp with node.name = "***" when creating it, its name will default to "ColorRamp" or "ColorRamp.00*" if there is already one in the material. So your call should read position_control = bpy.data.materials["TestMat"].node_tree.nodes["ColorRamp"].color_ramp.elements[0].position. Jun 18, 2020 at 11:25
• hi! @Gorgious thanks for answering, I renamed the color ramp because I am trying to Build a Complex Node group which will have many of the color ramps and for that purpose, each color ramps need to be called by their name, so that in further coding if I would like to call that color ramp it is always accessible to me in an easy way. Jun 19, 2020 at 2:57
• Then you need to add col_ramp.name = "col_ramp" after your initialize it, otherwise you can't access it by this name Jun 19, 2020 at 6:27

I don't think you need to redefine a whole class to acces the position of the element of your color ramp. You can just use a reference to the actual position, which is anyways always constrained between 0 and 1.

This eliminates the overhead of having to update one when the other gets updated.

Updated script to look for the node inside a node group :

bl_info = {
"author": "Rakesh Choudhary",
"version": (1, 0),
"blender": (2, 83, 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
from bpy.types import (
Operator,
Panel,
PropertyGroup,
)
from bpy.props import (
FloatProperty,
PointerProperty,
)

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

def create_material(self):
mesh = bpy.context.object.data
mesh.materials.clear()
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

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

# Creating Group Output Node

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

# Creating Diffuse Node
diffuse_node.location = (150, 100)

# Creating Glossy Node
glossy_node.location = (300, 250)

#Creating Color Ramp ------------------------------------------------------
col_ramp.name = "col_ramp"
col_ramp.location = (400, -300)
col_ramp.color_ramp.elements.remove(col_ramp.color_ramp.elements[0])

col_ramp.color_ramp.elements.new(0.750)
col_ramp.color_ramp.elements[0].color = (0,0,0,1)

col_ramp.color_ramp.elements[1].position = (1.0)
col_ramp.color_ramp.elements[1].color = (1, 1, 1, 1)

# Putting Node Group to the node editor
tree = bpy.context.object.active_material.node_tree
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

#Material ends here------------------------------

class TEST_MATERIAL_PT_layout_panel(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

active_object = context.view_layer.objects.active
if not active_object:
print("Select an Object")
return
active_material = active_object.active_material
if not active_material:
print("Add a material to the selected object")
return
node_tree = active_material.node_tree
if not node_tree:
print("Check 'Use Nodes' in the active material")
return
group = node_tree.nodes.get("Group")
if not group:
return
color_ramp_node = group.node_tree.nodes.get("col_ramp")
if not color_ramp_node:
print("Add a color ramp node to the active material")
return

position_control = color_ramp_node.color_ramp.elements[0]

row = layout.row()
row.prop(position_control, "position")

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()


Result :

• you are adding color ramp manually in this example, but in the basic script, I wrote - blender.stackexchange.com/questions/183011/… the color ramps are created in a material and the material will only be created when that operator(in which material l is created) is executed, what I exactly wanted is when I press that button the color ramp's element's position slider appears in the panel and that could be used to tweak the position of color ramp. Can you please please help me with this problem as I am not very much familiar with python. Jun 19, 2020 at 3:05
• There is no fundamental difference between adding a node by hand or by script, unless you change its name, you can access it the same way Jun 19, 2020 at 6:24
• I tried doing this But I am getting errors, Can you please help me with this I have updated my script which I tried and in which I got the error. Jun 20, 2020 at 16:31
• Updated ! Also there is no "ramp_pos" attribute in the color ramp element, it is called "position" Jun 21, 2020 at 11:24
• Yes I forgot to remove it :) Jun 21, 2020 at 13:43