Hey, happy to help I use Gitlab, but I haven’t published my node project yet, it isn’t useful yet and I want to get a lot more stuff working before I let anyone see it. I’m ashamed of how messy the code is right now
I don’t have a list of node-based projects on-hand, the only one I can think of that hasn’t been mentioned here is Modular-Tree, although its simple node-tree may be worth looking at.
How do you access the user input from the nodes? I assume the input is taken as properties (bpy.props?) but I haven’t been able to access them from outside the class?
Ah, well in the first place you should be working with instances of the class instead of the class itself. And as you mentioned earlier, the built-in classes aren’t all that useful unless you extend them in your own classes. Anyhow, it’s not much good to get the value of a class property! The node sockets are where the properties are stored, and you have to define these values as bpy.props
values (the convention is to name this property “default_value
”). You can define whatever functions for the class you may need, and a “tell_value()
” function can be helpful. There are a few built-in callback functions (including one that is unimplemented.) Sockets are themselves instantiated in the nodes’ init
function (don’t confuse this with __init__
which you usually can’t define for extensions of built-in classes).
Anyways, there’s one very annoying difficulty when it comes to getting from nodes to values: sockets are on the right (input) or left (output)… not both! So when I wrote a function for seeking back through the tree, I had to write a function for each node that says how to travel “through the node”, that is, from the output socket of the node to the corresponding input socket (data can travel through several nodes before reaching its destination, and it can be modified along the way, e.g. with a math node or something like that). So the function went something like this pseudocode:
socket = some socket
while(socket):
if socket in node.outputs: #this isn't a real property of node, but there's something like this IIRC
socket_b == node.traverse_socket(socket)
# node.traverse_socket is a custom function I have defined for all my nodes
if socket_b is None:
#(we've reached the end, so return the current socket)
break
else:
socket = socket_b
continue
if the socket in node.inputs:
if socket.connected == True:
connection = socket.from_connection
if connection.from_socket:
socket = connection.from_socket
continue
else:
break
Something like the above will eventually get you the first socket in the tree that is sending data into the tree by a value the user has set or by a node that provides data. Now you have to get the data from the socket, and if you define a default_prop
for each socket, thats easy enough. Another option is to define a tell_value()
function for each socket. Finally, the node itself may be what is relevant, in which case it’s still up to you to give it the appropriate property or function.
I use this function for going backward through the tree to query values. This is a good way to work when your node graph has an output node that gathers input from multiple input nodes.
As it turns out, my node-tree is a little backwards… it has a single root node that has a lot of children and those children can have children, so it is very much structured like a tree. So I wrote a very clever function for walking through the tree and returning every traversable line through the tree. This was very hard to write, and I won’t attempt to render it here as pseudo-text; it was essentially a Python version of this:
(see
Tree traversal - Wikipedia)
I’d love to share my code with you if it will help, but I will need to do it in a paste-bin or something since it’s not ready to publish yet! I want to have a minimum viable product and a lot more error handling done before I do that!
Honestly, this is one of the more difficult parts of the Python API that I’ve used. It was very challenging for me to get as far as I did! And I’ve been doing this a little while. (You might be wondering why I haven’t finished it – I don’t get much free time for this and I have been focusing it on improving my Rigging Addon… I think I can have it ready for 1.0 by the end of the year ).
I understand that this is probably a little difficult to follow – it’s hard to write about this in the abstract. It’s also not written very clearly (it’s 1 AM now). So let me know if you need me to explain anything further!
EDIT: In fact, one big thing that I forgot is this: most of the time you will use bpy.props
that you define in the node to send data into the tree, only unconnected input sockets are really useful for giving input from a socket. Finding the “first” socket, as I explained above, may be useful simply for finding the first input node. The node and the socket each have draw
functions for drawing their properties (just like operators) and you can draw a property in the UI and modify it and query it. That’s a good way of getting information into the tree. You can also have nodes that initialize the values at creation or use update callbacks if you want to get fancy.