Refactoring script to blender 4.0, node_tree.nodes key error

Hi everyone, I am refactoring a script I wish to use in 4.0, which I believe to be written in 2022? The script takes in an image loaded in to blender and puts it into a color ramp. Here’s an example image and the code:

sunramp_orange_01b

import bpy
from bpy import data as D

imageFile = "Image.png"
materialName = "Material"
colorCount = 15

# Get the reference to the specified image
img = D.images[imageFile]

# Determine height and width of the image
width = img.size[0]
height = img.size[1]

# Get the reference to the specified material
material = bpy.data.materials[materialName]

# Get the node collection of the material
nodes = material.node_tree.nodes

# Create a color ramp node
color_ramp = material.node_tree.nodes.new("ShaderNodeValToRGB")

# Get a reference to the colorRamp node
cr=D.materials[materialName].node_tree.nodes["ColorRamp"].color_ramp

# Set interpolation between color stops to "CONSTANT"
cr.interpolation = "CONSTANT"

# Convert sRGB to linear RGB
def sRGB2linear(s):
    if s <= 0.0404482362771082:
        lin = s / 12.92
    else:
        lin = ((s + 0.055) / 1.055) ** 2.4
    return lin

# Iterate through the colors
for x in range(colorCount):
    # Define the position of the pixel we want to read out
    target = [round(x * (width / colorCount) + width / colorCount/2), round(height / 2)]
    # Determine the index of the pixel to be read out (#RGBA)
    index = (target[1] * width + target[0]) * 4
    if (x > 0 and x < colorCount -1):
        # Add a new position to the ColorRamp
        cr.elements.new(position = 1 / (colorCount - 1) * x)

    # Read the color at the given index and set the color in the ColorRamp
    cr.elements[x].color=(
        sRGB2linear(img.pixels[index]), # R
        sRGB2linear(img.pixels[index + 1]), #G
        sRGB2linear(img.pixels[index + 2]), #B
        1, #A
    )

Here’s the order of operations on how this is supposed to function:

  1. Dimensions of the image are determined.

  2. A reference to the specified material is created and a color ramp node is created there.

  3. In the loop, using x number of color values specified, the pixel for reading the color is determined.

  4. Color values are transferred directly to the new positions of the ColorRamp created at the same time.

I keep getting a key error: KeyError: 'bpy_prop_collection[key]: key "ColorRamp" not found'. Which I believe to be blender internally changing this key? It adds the color ramp into a material fine but gets stuck on this part. Is there any specific guide on how to refactor this code? Or key-renaming guide? Any guidance would be appreciated as I couldn’t really find much googling around.

Well if Python is telling you the key isn’t found, print out what keys are present and see for yourself:

for n in D.materials[materialName].node_tree.nodes:
    print(n)
<bpy_struct, ShaderNodeOutputMaterial("Material Output") at 0x000002B977F03CA0>
<bpy_struct, ShaderNodeBsdfPrincipled("Principled BSDF") at 0x000002B977F03920>
<bpy_struct, ShaderNodeTexImage("Image Texture") at 0x000002B90E763B88>
<bpy_struct, ShaderNodeValToRGB("Color Ramp") at 0x000002B90E762E08>

You would see that the node is titled “Color Ramp” with a space by default.

That fixed the issue, thank you. I did not realize the keys could be enumerated like that. Is there no resource listing these keys out there?

Not really. Also keep in mind that the name will change if more than 1 type of that node has been added to the tree:

<bpy_struct, ShaderNodeValToRGB("Color Ramp") at 0x000002794AD17708>
<bpy_struct, ShaderNodeValToRGB("Color Ramp.001") at 0x000002794AD16388>
<bpy_struct, ShaderNodeValToRGB("Color Ramp.002") at 0x000002794AD16E08>

In your case here, just use the Node directly:

# Create a color ramp node
color_ramp_node = material.node_tree.nodes.new("ShaderNodeValToRGB")

# You can lookup by node name like this:
# cr=D.materials[materialName].node_tree.nodes[color_ramp_node.name].color_ramp

# But, you already have the node, just use it directly instead of looking it up by name:
cr=color_ramp_node.color_ramp