1
$\begingroup$

I am trying to write a script where nodes are spawned and sorted according to where they need to be in the node editor. Thanks to help from other users, I have learned to move the nodes properly. However, the answers they provided used a hard-coded list:

atomicNodes = ['Texture Coordinate', 'Mapping', ...]
imageTextures = ['Image Texture', 'Image Texture.001', 'Image Texture.002']

I have a method of returning a list of nodes that exist in the scene, but they aren't instantiated as a list when I call them:

for i in atomicNodes:
    nodes[i].location.x = locx
    locx += nodes[i].width + 50
    nodes[i].location.y = 0

for i in imageTextures:
    nodes[i].location.y = locy
    locy += nodes[i].height + 200
    nodes[i].location.x = nodes[1].location.x

When I go ahead and just print out the nodes, in the scene using:

bpy.data.materials['Material'].node_tree.nodes

it gives me this in the console:

Console Output

Using the Python documentation for listing dictionary keys in this format:

nodes = bpy.data.materials['Material'].node_tree.nodes
nodeValues = nodes.values()
nodeKeys = nodes.keys()

then changing the variable atomicNodes to

atomicNodes = nodeKeys

Makes it run correctly.

However, when I try to do the same thing for my list of image textures, I'm having some issues. I get this error when I try to use the filtered list of image nodes instead of the 'nodes' variable: enter image description here

Here are the scripts on GitHub, I am using the latest release candidate for 2.79 because I am interacting with the principled shader, but I have been having this issue while testing in stable versions of 2.78 too.

Edit: The issue seems to be that while the nodes variable

nodes = bpy.data.materials['Material'].node_tree.nodes

is a dictionary already by default, when I get the Image Texture nodes it is converted to a list, which I obviously can't grab keys from.

When I use my method to collect the Image Textures, it places them into a List already. Unfortunately there is a lot of extra content that I don't need:

[bpy.data...nodes["Image Texture"], bpy.data...nodes["Image Texture.001"], bpy.data...nodes["Image Texture.002"]]

I only want what is inside of the quotation marks of this list, is there any way of doing that aside from regular expressions?

$\endgroup$
  • $\begingroup$ Need to see more code, lie the "use the filtered list of image nodes instead of the 'nodes'" part and the part in the traceback. $\endgroup$ – cmomoney Aug 22 '17 at 16:04
  • $\begingroup$ For sure: I linked my full code on GitHub at the bottom just after you left your comment, but I can move it into the main question in a minute or two. $\endgroup$ – Rug Aug 22 '17 at 16:14
  • $\begingroup$ Is your question about how to get the names of all nodes in a list? @Rug $\endgroup$ – brockmann Aug 22 '17 at 16:25
  • 1
    $\begingroup$ imageNodeDict is a list not a dictionary. Hence you cannot access it using imageNodeDict["Node Name"] , only by index. The error message indicates in your code, (and by the line numbers prob not github code as it stands now), you are accessing a bpy collection like say material.node_tree.nodes[node] where node is a ShaderNodeTexImg (possibly a member of imageNodeDict), rather than an index (int) or by name (string). $\endgroup$ – batFINGER Aug 22 '17 at 17:06
  • 1
    $\begingroup$ BTW to make a dictionary use the items() method which returns a key, value pair, eg tex_nodes_dict = {k : n for k, n in tree.nodes.items() if n.type == 'TEX_IMAGE'} $\endgroup$ – batFINGER Aug 22 '17 at 17:21
1
$\begingroup$

You don't need their names in a list. You can iterate through Material.node_tree.nodes collection which allows to test if the node type is Image Texture and set the position attribute in one go.

Operator example based on Templates > Python > Operator Node:

import bpy

class NodeOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "node.simple_operator"
    bl_label = "Simple Node Operator"

    @classmethod
    def poll(cls, context):
        space = context.space_data
        return space.type == 'NODE_EDITOR'

    def execute(self, context):
        tree = context.space_data.node_tree

        # set the location if node type is
        for n in tree.nodes:
            if n.type == "TEX_IMAGE":
                print (n.location.x)
                n.location.x += 50

        # filter by type
        image_texture_nodes = [n for n in tree.nodes if n.type == "TEX_IMAGE"]

        # get the names
        image_texture_names =  [n.name for n in image_texture_nodes]

        # check the console
        print (image_texture_nodes)
        print (image_texture_names)

        return {'FINISHED'}


def register():
    bpy.utils.register_class(NodeOperator)


def unregister():
    bpy.utils.unregister_class(NodeOperator)


if __name__ == "__main__":
    register()
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.