Help: Dynamic NodeGroup Updates: Managing Link Movements with Socket Name

I am working on a certain Add-On, part of which is the dynamic creation of Node groups.

I use the following code to update the NodeTree of each Node when the underlying NodeTree is updated.

         for node in self.nodes:
             node.node_tree = self.node_tree

The thing is when I update the NodeTree, new output sockets should sometimes be created between the old ones, and when I do that, the old links that were connected to the old outputs stay in place. (Although they should go down with the outputs.)
Is there a way to make them move together with the output sockets?

I think it would be wise to do it socket name based, because in my case each socket has a unique name, but any other idea would be welcome.

By the way, I would prefer if the answers you offer do not include the use of the NodeTree where the connected nodes are located, since it will be too heavy for performance to search for it for each node.

This should depend on socket identifiers.

1 Like

Can you please elaborate on what you mean?

I run into this problem sometimes as well, and it’s based on that the links between nodes are using the unique socket property socket.identifier to decide which socket the link is linked to.

An example I ran into recently, is if I swap out this first node for the second, I get an extra connection into the ‘second’ socket called Selection, which is not what I expected.

If we look at the unique identifiers for each of the nodes though, we can see what might be going wrong.

Node 1 (Style Presets):

>>> list(input.identifier for input in node.inputs)
['Input_1', 'Input_3', 'Input_2', 'Input_5', 'Input_6', 'Input_7']

Node 2 (Style Spheres)

>>> list(input.identifier for input in node.inputs)
['Input_0', 'Input_1', 'Input_2', 'Input_3', 'Input_4', 'Input_5', 'Input_6']

The ‘second’ input in the new node group has the identifier Input_1, whereas in the first node the link was to the first input, which also had the identifier Input_1.

These identifiers are dependent on the order in which you create them for the node group, and I believe stay that name regardless of how you rearrange or delete & add new inputs.

You could try capturing the links for the inputs, removing all of the links, changing out the node group, then recreating the links base on the node names, which should get around the creating of links to the ‘incorrect’ sockets.

1 Like

This motivated me to finally go through and fix the issue that I had been running into, and this is how I did it. Works pretty well for me, hopefully it can help you out as well!

Importantly I am capturing the sockets on the node that we are changing by name, and the sockets that those sockets are linked to just by their actual socket. When we go and rebuild all of the links, you can just pass the socket in instead of having to look it up on the node etc.


def change_style_node(object, style):
    # get the node group that we are working on, to change the specific style node
    group = get_mod(object).node_group
    link = group.links.new
    node_style = get_style_node(object)

    # capture input and output links, so we can rebuild the links based on name
    # and the sockets they were connected to
    # as we collect them, remove the links so they aren't automatically connected
    # when we change the node_tree for the group
    input_links = []
    output_links = []
    for input in node_style.inputs:
        for input_link in input.links:
            input_links.append((input_link.from_socket, input.name))
            group.links.remove(input_link)

    for output in node_style.outputs:
        for output_link in output.links:
            output_links.append((output.name, output_link.to_socket))
            group.links.remove(output_link)

    try:
        material = node_style.inputs['Material'].default_value
    except KeyError:
        material = None
    # append the new node tree, and swap out the tree that is used for the group
    group_new = append(styles_mapping[style])
    node_style.node_tree = group_new
    # do some formatting and cleanup the node name for easier reading
    node_style.name = group_new.name
    node_style.label = format_node_name(node_style.name)

    # rebuild the links based on names of the sockets, not their identifiers
    for input_link in input_links:
        link(input_link[0], node_style.inputs[input_link[1]])
    for output_link in output_links:
        link(node_style.outputs[output_link[0]], output_link[1])

    if material:
        try:
            node_style.inputs['Material'].default_value = material
        except KeyError:
            # the new node doesn't contain a material slot
            pass
1 Like