Custom Nodes: How to validate a link? I'm developing an add-on that uses a custom node tree. I want to be able to set a link between nodes as being valid or not:



NodeLink has the property is_valid which controls also how it's drawn (as pictured above). I want to use this value, but something keeps changing it to True any time the node tree updates.

Node's have the insert_link(link) method which you can override. It seems to be the first thing called when a new link is made. This would be an ideal time to validate the link and set the property.

After insert_link(link) returns, the NodeTree.update() method is called on the parent NodeTree which you can also override. What ever value you assigned to NodeLink.is_valid is still the same at this point.

I don't think any other functions are called inside the custom tree after that? But once the UI updates the NodeLink.is_valid value has returned to being True regardless of what it was set to.

The same thing happens if you change the value from the python console. It will draw in the red color and be False if you check the value. But as soon as any new link or update is made to the tree, it goes back to being True again.

Something else must be determining if a link is valid each time it updates? But I cannot find anything in the documentation that says what this is or how to interact with it??? But, it seems like this should be something that can be done given there is a built-in value for it and the UI checks that value when drawing it...

So how do you validate links in a custom node tree?

# First function called in custom tree when a link is made
def Node.insert_link(link):
    # Validate the link
    link.is_valid = False

# Called after insert_link is finished
def NodeTree.update():
    # Link valid value is still set from above
    link.is_valid
    False

# After update link valid value has been set to True
link.is_valid
True


I'm currently working around it with a custom bool property, which essentially just duplicates the existing property. Which works fine for preventing problems with invalid connections, but the state of the link isn't displayed in the UI.

I suppose an acceptable solution would be a way of making the link draw in red using my custom property. It just seems really silly when the property already exists and does the thing I want, but something keeps on changing it on me.

ntree_validate_links(bNodeTree *ntree) in the C code is the thing that causes it to change and is called as essentially the last thing when updating a node tree. It does three things, first is sets the flag to valid, second it checks for a cyclic link and will set the flag to invalid if found. Lastly it checks for ntree-&gt;typeinfo-&gt;validate_link which I'm assuming is a function pointer and calls that to set the flag.

I'm not seeing anything in the RNA node stuff to interact with it though.
I'm not sure this is what you're looking for.

In Sverchok we add a property to the socket class, called 'other', which returns the socket instance of the other socket, and even iterates back through "ReRoute" nodes. 

@property
def other(self):
    return get_other_socket(self)


This lets us do a simple test when appropriate.

if not socket.is_output:
    if not (socket.bl_idname == socket.other.bl_idname):
        ... # handle the mismatch in socket types


You figure the logic out for input sockets vs output sockets. Some node systems (Sverchok included) don't allow a node to accept multiple links on an input socket -- By design.

modified from sverchok/data_structure.py

def get_other_socket(socket):
    """
    Get next real upstream socket.
    Will return None if there isn't a another socket connect
    so no need to check socket.links
    """
    if not socket.is_linked:
        return None
    if not socket.is_output:
        other = socket.links[0].from_socket
    else:
        other = socket.links[0].to_socket

    if other.node.bl_idname == 'NodeReroute':
        if not socket.is_output:
            return get_other_socket(other.node.inputs[0])
        else:
            return get_other_socket(other.node.outputs[0])
    else:
        return other