List All Nodes With No Filter?
-
Hi,
I created a thread before on listing all nodes but it is limted to having a filter property.
I tried using an empty dictionary but it errors.
I just want to have a list of all the nodes in a given graph/material (like what doc.GetMaterials() does) and perform my own filtering later on.
Is this possible?
-
Hey @bentraje,
Thank you for reaching out to us. Yes,
GraphModelHelper.ListAllNodes
requires at least one attribute to be set in the data dictionary. The method description also tries to conveys this, but erroneously uses the word 'can' where 'must' is meant:The passed
DataDictionary
can have one or multiple data defined.Meant is here:
The passed
DataDictionary
must have at least one entry. When multiple entries are defined, only nodes matching all of them will be yielded.I will update that documentation. Regarding your question, I am afraid that the answer I gave in the old thread does not change much:
[...] write your own little node traversal function.
I understand that this can be a bit confusing, but in our APIs users are often expected to do data traversal themselves. In this case, call
GraphNode.GetChildren
recursively, plus some fluff of whatever one wants to do - printing trees, filtering nodes, etc. Find a code example below, but it is more or less the same as what I posted in the old thread.Cheers,
FerdinandResult for the default graph of the standard node space:
This will already result in +400 lines because even for such seemingly simple graph, there are all the ports of the nodes which are not shown by default.
Get all nodes: 'Kind 1 node with the id '' at 0x000001A619A653D0 'Kind 4 node with the id '>' at 0x000001A619A54B50 'Kind 2 node with the id '<' at 0x000001A619A6D090 'Kind 1 node with the id 'material' at 0x000001A619A6D150 'Kind 4 node with the id '>' at 0x000001A619A66910 'Kind 16 node with the id 'materialout' at 0x000001A619A78150 'Kind 16 node with the id 'aovs' at 0x000001A619A69F10 'Kind 16 node with the id 'spdlevel' at 0x000001A619A69D10 'Kind 16 node with the id 'roundgeometry' at 0x000001A619A565D0 'Kind 16 node with the id 'spd' at 0x000001A619A56590 'Kind 16 node with the id 'displacementheight' at 0x000001A619A56790 'Kind 16 node with the id 'displacement' at 0x000001A619A56810 ... Get all in- and output ports: 'Kind 16 node with the id 'materialout' at 0x000001A619A6D410 'Kind 16 node with the id 'aovs' at 0x000001A619A765D0 'Kind 16 node with the id 'spdlevel' at 0x000001A619A5E010 'Kind 16 node with the id 'roundgeometry' at 0x000001A619A5D950 ...
Code:
"""Demonstrates how to traverse nodes in a node graph. Must be run from the Script Manager with a node material selected which has a node graph in the currently active node space, i.e., a material which produces an output for the currently active renderer. """ import c4d import maxon import typing doc: c4d.documents.BaseDocument # The active document. def main(): """Runs the example. """ # Get the graph for the active material in the active node space. material: c4d.BaseMaterial = doc.GetActiveMaterial() if material is None: raise RuntimeError("No material selected.") nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference() if nodeMaterial is None: raise MemoryError(f"Could not access node material for {material}.") graph: maxon.GraphModelRef = nodeMaterial.GetGraph(c4d.GetActiveNodeSpaceId()) if graph.IsNullValue(): raise RuntimeError(f"There is no graph for {c4d.GetActiveNodeSpaceId()} in {material}") # Define our little traversal function. def GetDescendants(node: maxon.GraphNode, mask: tuple[maxon.NODE_KIND] = tuple()) -> typing.Iterator[maxon.GraphNode]: """Yields all descendants of #node including #node itself whose node kind matches #mask. """ if len(mask) == 0 or node.GetKind() in mask: yield node for child in node.GetChildren(): for descendant in GetDescendants(child, mask): yield descendant # A function to pretty print nodes. PrintNode = lambda node: print (f"'Kind {node.GetKind()} node with the id '{node.GetId()}' at", f"0x{hex(id(node)).upper()[2:].zfill(16)}") # Use our function to traverse nodes ... # Iterate over all nodes below and including root. print("\nGet all nodes:") for node in GetDescendants(graph.GetRoot()): PrintNode(node) # Iterate over all input and output ports below root. print("\nGet all in- and output ports:") for node in GetDescendants(graph.GetRoot(), (maxon.NODE_KIND.INPORT, maxon.NODE_KIND.OUTPORT)): PrintNode(node) if __name__ == "__main__": main()
-
RE: Yes, GraphModelHelper.ListAllNodes requires at least one attribute to be set in the data dictionary.
Gotcha. Thanks for the clarification.
Anyway, the suggested alternative (i.e. write a separate iterator) still works as expected. Same as before.