I’m currently setting up an open-source Python package for Maya that would allow you to set up more complex node trees in Maya through code without doing the multitude of ‘createNode’ and ‘connectAttr’ functions. Note that currently it’s still in a very early stage (consider it pre-alpha I suppose?) but I’m looking for some more tips on the overall design of the system.
In short, you write a snippet of code like (very simple example):
n = Nodex('pSphere1.t') + Nodex([10, 0, 0])
And it will result in the sphere’s translate being connected to a plusMinusAverage node and directly adding 10 to the translateX value.
Since it creates a set of nodes with connection once you move the sphere around the output of the node tree updates (just like normally)
Especially have a look at the code samples at the bottom on what I mean with ‘more expressive’ way of defining node trees.
Currently I’m working in a test-driven-development manner and the current set of tests all pass.
Which means the current implementations of addition, subtraction and conditions (equal, not equal, bigger than, smaller than) so far seem to be fine for the simpler cases.
Have a look at the current tests: https://github.com/BigRoy/nodex/blob/master/nodex/tests.py
Here would be my primary list of questions:
From a usage perspective:
Has something similar been done before? And is that available online somewhere?
Do you think you could benefit from a solid implementation of Nodex in your own workflow?
Would you be willing to provide examples on how you’d like to code a node tree in this manner? (Or is this your expected way)
From a coding perspective:
I’m looking for a way to make the Nodex work in a more abstract manner and am considering separating the ‘data reference’ it holds into its own “DataTypes” structure. So whenever it references a Boolean value or attribute it’s Nodex would be a Nodex reference the Boolean datatype. By doing this it should be easier to implement corresponding functionality for adding/subtracting for this data type. Plus it would allow me to define more complex types (eg. Matrix calculations) without making the Nodex class a big set of ‘if this type, elif this type, etc’ statements. Does anyone have any pointers in that regard?
I’m looking for a license to go with this package that would be open enough for everyone to pick it up and make it a collaborative process (when someone extends/modifies it then it will still be something that we could all benefit from) to keep it open. Not sure if that clarifies it, but any pointers/tips on licensing are greatly appreciated.
And of course feedback in general would be sweet-sauce!
Just wanted to post an update here that I’ve been prototyping some more with Nodex and released version 0.2.1.
Within this release I’ve abstracted the Nodex class just like how the PyNode works in Pymel. It acts as an abstract factory and based on the input data it instantiates the relevant class for the datatype.
Thus Nodex([0, 0, 1]) becomes a Vector, to be exact: Vector((Integer(0), Integer(0), Integer(1)).
Interesting stuff! A great idea to make a DSL for this very specific problem set.
Am I correct that if you wanted to maintain a set of nodes representing the networks you’ve created and to work with them - traversing the tree or calculating costs or something like that – you would have to do it using vanllla maya? Maybe you could extend Nodex with traversal functions ( “.parent()”, “.children()” etc - to make it easier to hop around in a graph. It would particularly nice if you could create the same data structure from existing nodes, so you could use the same code for creating new networks and managing existing ones.
On a related note, you might also be able to get rid of utils.nodeHelper() function and replace it with a bevy of node-type specific classes. The classes would work like PyNodes - they’d be tied to specific scene objects, editing them with properties or functions and reflecting their current state in the scene so you never had to manage parallel trees of ‘my data structure says X, maya’s says Y’.
Overall, this actually feels a lot like what I’m fiddling around with in mGui: a mini-language tailored to making problem solving easier for a specific type of problem. It’s a great concept. I bet you’ll find out a bunch of interesting things as the API evolves - I know I did ! - but it’s definitely a worthwhile endeavor.
Thanks for showing interest; it’s highly motivating!
Currently the nodex has methods like attr() and node().
If the Nodex references an Attribute this way you’ll be able to access the relevant node/attribute (which returns the Pymel variant).
In theory you could already do:
n = Nodex('pSphere.translate')
n += 1
n.node().inputs()
This also means if you do n.value() it will always return the up-to-date value in the scene (since it’s just getting the value then and there).
This way you are very close to accessing the Pymel stuff for the node graph. I thought since it’s already widely used and praised for its ease of access and readability why not stay close to it.
So no need to re-invent the PyNode I suppose.
It would particularly nice if you could create the same data structure from existing nodes, so you could use the same code for creating new networks and managing existing ones.
This sounds like you would want a way to reverse engineer an already created node graph into code. In the core that’s close to storing the nodes in a exportable format and recreating it. (You could use .ma or .mb; or a custom export/import to a format like JSON). If you have any other thoughts on that (how would it work differently?) then I would love to hear more about it.
Nevertheless I was thinking about adding other functionality to Nodex to manage the node graph, like context managers that create containers of the newly defined nodes. And since it’s hard to name (naming conventions anyone?) your nodes while doing such expressions you could end up doing something like:
with nodex.Container(name="complexOperation") as cont:
with nodex.NodeNamingScheme(studioNaming):
n = Nodex('pSphere1.translate')
result = (n + 5) * 0.5
cont.addOutput(result)
Using context managers to help you design clean node trees quickly.
These are still just ideas and that last snippet is of course pseudo-code.
Btw, I’ve seen mGui before! Though I never took the leap into it since that’s when I went straight into the Qt era. Nevertheless I like the ideas you have on the bindings/observables. Keep it up!
This also means if you do n.value() it will always return the up-to-date value in the scene (since it’s just getting the value then and there).
This way you are very close to accessing the Pymel stuff for the node graph. I thought since it’s already widely used and praised for its ease of access and readability why not stay close to it.
So no need to re-invent the PyNode I suppose.
Some people are all Pymel, some people are kind of against it on principle. If you wanted a fallback you can implement something very similar with vanilla maya and property descriptors. But what I was really thinking was to extend the Nodex mini-language so that you could ‘reason’ over a node graph using Nodexes the way you might iterate over a set of nodes in an XML document with ElementTree or BeautifulSoup, which make it (fairly) simple to walk a tree structures.
Context managers do seem like a cool idea for this. You could probably trick it out so that closing the context does all of the creations and connections in one go, instead of doing them incrementally - that way you could do a nice cleanup if something went wrong without manually tracking all the undo steps needed.