4
$\begingroup$

I need to obtain just the index value from node_tree.links[...].from_socket, I am able to see it like for example

# this is example
bpy.data.materials["SomeMat"].node_tree.nodes["SomeNode"].outputs[0]

but I would like to save just the index value [0] to variable...how to? I am lost seeing no option for this... :(

For example there is a way how to find out the name of the ".from_socket" like this:

 bpy.data.materials["SomeMat"].node_tree.links[0].from_socket.name

...and then I get its name, for example output socket called "BSDF". But is there something that returns index number of used output instead of its name as stated above? Cos if I use:

# BTW this gives me exactly what I would normaly needed like 'bpy.data.materials["SomeMat"].node_tree.nodes["SomeNode"].outputs[0]' but this time I need to get only the last index value, it is: [0]
idx = bpy.data.materials["SomeMat"].node_tree.links[0].from_socket

...then the variable is whole object, not just the index value :(

The reason why I need this is because I am copying nodes with their links and all settings from one material to another one, everything work except links, it works OK if there is just one instance of concrete input socket, if there are two of the same name like in Mix Shader (it has two inputs called the same as "shader") then my script does not work right cos it uses socket names instead of their index values like:

links.new(nodes[i.from_node.name].outputs[i.from_socket.name], nodes[i.to_node.name].outputs[i.to_socket.name])

So my new working script would be then:

# see .INDEX is meant just as kind of signature for the index number I am talking about thru all this post
links.new(nodes[i.from_node.name].outputs[i.from_socket.INDEX], nodes[i.to_node.name].outputs[i.to_socket.INDEX])

And all of this just because the most common solution does not work at all, this:

links.new(i.from_socket, i.to_socket)

If I use this then no connections are made...BUT WHY???

BTW I did try to solve it by work around of renaming MixShader socket inputs [1] and [2] that uses the same name of "Shader" to something like "A" and "B" but as soon as I do it Blender crashes right after successful renaming (BTW why in the world they uses the same name at all causing this kind of problems??? gee...)...so no go this way :(

P.S.: I need only script solutions, thanx

$\endgroup$
3
  • $\begingroup$ sorry but that is not what I want, I can also trace whatever link or node connection I need, that is no problem, but I am seeking for a way to save inputs/outputs index numbers to variable $\endgroup$
    – bublible
    May 19, 2014 at 19:35
  • $\begingroup$ Oops, sorry. I misread. $\endgroup$
    – gandalf3
    May 19, 2014 at 19:36
  • $\begingroup$ one thing that comes to my mind immediately was just take what links.from_socket gives you, make it ordinary string like 'bpy.data.materials["SomeMat"].node_tree.nodes["SomeNode"].outputs[0]', then split it into array and then there would be easy way around but I found no option how to convert it to a string, cos simple str(bpy.data.materials["SomeMat"].node_tree.nodes["SomeNode"].outputs[0]) does not work: it returns socket name!!! $\endgroup$
    – bublible
    May 19, 2014 at 20:55

4 Answers 4

6
$\begingroup$

One of your examples is -

links.new(nodes[i.from_node.name].outputs[i.from_socket.name],
          nodes[i.to_node.name].outputs[i.to_socket.name])

Do you notice that you are trying to connect an output socket to an output socket. You would want to connect the from_node.outputs to the to_node.inputs.

Also of note is that as you know the socket names are rarely unique, which is the biggest cause of trouble here, but a socket also has a property called identifier that is a unique name for the node. I don't think you can use it as an array index but by counting your way through all sockets of the node you can find the same identifier and use your count as the index. The socket identifier property was added in 2.66 if you need to watch compatibility.

Another approach to get the socket index number is with something like -

matLinks = bpy.data.materials["SomeMat"].node_tree.links
socketID = matLinks[0].from_socket.path_from_id()[-2:-1]

So path_from_id() will return something like nodes["Mix Shader"].inputs[1] as a string, you can then use array access to get the second last character.

Most nodes only have a few inputs and outputs but to properly handle groups and script nodes which could have more than 10 sockets you would want to search for the last '[' and ']' to define your string extraction.

$\endgroup$
2
  • $\begingroup$ that was just my mistake woth copying text when I forgot to overwrite second "outputs" to "inputs", in real script it is written as it should be (see I am scripting in another PC so I simply copying lines therefore mistake happened) ;) I did not try it yet but I think your second solution looks like it is exactly what I was looking for...thanx, will try it + yea, I know of .identifier, .value, .get etc. but their output was useless for me just like you said $\endgroup$
    – bublible
    May 20, 2014 at 9:52
  • $\begingroup$ + also as you can see I said in my previous comment absolutely the same as you in your last line so I'm happy we are programmatically thinking the same...I just did not know how to convert function statement to poor string :D $\endgroup$
    – bublible
    May 20, 2014 at 10:22
4
$\begingroup$

I believe the Principled BSDF node didn't exist when the question was asked and answered. This node has more than 10 inputs so the parsing presented in both methods doesn't work with 2 digit node indices (it wraps back to 0 at index n° 10).

Here is a proposition to get the socket index of a node with an arbitrary number of sockets:

socket.path_from_id()[:-1].split("[")[-1]
$\endgroup$
1
$\begingroup$

list.index

enter image description here test material

If we slice a full list of the inputs / outputs then

>>> link = D.materials['Material'].node_tree.links[0]

>>> link.from_node.outputs[:]
[bpy.data.materials['Material'].node_tree.nodes["Principled BSDF"].outputs[0]]

>>> link.to_node.inputs[:]
[bpy.data.materials['Material'].node_tree.nodes["Material Output"].inputs[0], bpy.data.materials['Material'].node_tree.nodes["Material Output"].inputs[1], bpy.data.materials['Material'].node_tree.nodes["Material Output"].inputs[2]]

can get the socket indices via

>>> link.from_node.outputs[:].index(link.from_socket)
0

>>> link.to_node.inputs[:].index(link.to_socket)
2
$\endgroup$
0
$\begingroup$

Well, inspired/directed/advised (?) by wise words from @sambler my FINAL WORKING CODE is this:

links.new(nodes[i.from_node.name].outputs[int(i.from_socket.path_from_id()[-2:-1])], nodes[i.to_node.name].inputs[int(i.to_socket.path_from_id()[-2:-1]])

Thanx once again, @sambler ;)

$\endgroup$
1
  • $\begingroup$ possibly worth mentioning that nodes[i.from_node.name] is i.from_node $\endgroup$
    – batFINGER
    Jul 13, 2021 at 18:22

You must log in to answer this question.

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