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$
6
  • $\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, 2017 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, 2017 at 16:14
  • $\begingroup$ Is your question about how to get the names of all nodes in a list? @Rug $\endgroup$
    – brockmann
    Aug 22, 2017 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, 2017 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, 2017 at 17:21

1 Answer 1

3
$\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$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .