Last active
October 5, 2023 18:29
-
-
Save BigRoy/eddac6a93ad4ff233e2757c6bbccf3da to your computer and use it in GitHub Desktop.
Autodesk Maya Python list or query all children including each instance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""When using maya.cmds.listRelatives with allDescendents=True it will only return the first instanced child. | |
Below are some example functions that correctly return all instanced children where they are "somewhat" optimized to rapidly return a result as opposed to slow recursive queries. | |
""" | |
import maya.api.OpenMaya as om | |
from maya import cmds | |
import time | |
import re | |
def iter_parents(node): | |
"""Iter parents of node from its long name. | |
Note: The `node` *must* be the long node name. | |
Args: | |
node (str): Node long name. | |
Yields: | |
str: All parent node names (long names) | |
""" | |
while True: | |
split = node.rsplit("|", 1) | |
if len(split) == 1 or not split[0]: | |
return | |
node = split[0] | |
yield node | |
def get_highest_in_hierarchy(nodes): | |
"""Return highest nodes in the hierarchy that are in the `nodes` list. | |
The "highest in hierarchy" are the nodes closest to world: top-most level. | |
Args: | |
nodes (list): The nodes in which find the highest in hierarchies. | |
Returns: | |
list: The highest nodes from the input nodes. | |
""" | |
# Ensure we use long names | |
nodes = cmds.ls(nodes, long=True) | |
if len(nodes) < 2: | |
return nodes | |
lookup = set(nodes) | |
highest = [] | |
for node in nodes: | |
# If no parents are within the nodes input list | |
# then this is a highest node | |
if not any(n in lookup for n in iter_parents(node)): | |
highest.append(node) | |
return highest | |
def list_all_children(nodes): | |
"""Fast, but slow when nesting is very deep.""" | |
result = set() | |
children = set(cmds.listRelatives(nodes, fullPath=True) or []) | |
while children: | |
result.update(children) | |
children = set(cmds.listRelatives(children, fullPath=True) or []) - result | |
return list(result) | |
def list_all_children2(nodes): | |
"""Invalid because it will ignore a child if it's in 'nodes' | |
whilst it was a child of another node in the list.""" | |
nodes = cmds.ls(nodes, long=True) | |
lookup = set(nodes) # parent lookup | |
hierarchy = cmds.ls(nodes, dag=True, long=True, allPaths=True) | |
children = [] | |
for node in hierarchy: | |
if node in lookup: | |
# Ignore self | |
continue | |
if not any(node.startswith(x) for x in lookup): | |
# Only include if it has a parent in the original nodes | |
continue | |
children.append(node) | |
return children | |
def list_all_children4(nodes): | |
""" | |
Fastest with few input `nodes` (<1000) | |
""" | |
roots = get_highest_in_hierarchy(nodes) | |
# Escape the | in the node paths, this is faster than re.escape() | |
# And also join an extra "\|" to make sure it will only capture | |
# its children. | |
src = "|" | |
to = r"\|" | |
prefixes = ["".join([n.replace(src, to), to]) for n in roots] | |
rx = re.compile(''.join(['^(?:', '|'.join(prefixes), ')'])) | |
hierarchy = cmds.ls(roots, dag=True, long=True, allPaths=True) | |
return [child for child in children if rx.match(child)] | |
def list_all_children5(nodes): | |
""" | |
Fastest with many input `nodes` (1000+) | |
""" | |
sel = om.MSelectionList() | |
traversed = set() | |
iterator = om.MItDag(om.MItDag.kDepthFirst) | |
for node in nodes: | |
if node in traversed: | |
# Ignore if already processed as a child | |
# before | |
continue | |
sel.clear() | |
sel.add(node) | |
dag = sel.getDagPath(0) | |
iterator.reset(dag) | |
iterator.next() # ignore self | |
while not iterator.isDone(): | |
path = iterator.fullPathName() | |
if path in traversed: | |
iterator.prune() | |
iterator.next() | |
continue | |
traversed.add(path) | |
iterator.next() | |
return list(traversed) | |
def list_all_children6(nodes): | |
"""Quite slow but accurate - just use Select > Select Hierarchy as trick..""" | |
sel = cmds.ls(sl=1) | |
cmds.select(nodes, hierarchy=True, replace=True) | |
hierarchy = cmds.ls(selection=True, long=True) | |
cmds.select(sel, replace=True) | |
return hierarchy | |
members = cmds.ls(sl=1, long=True) | |
s = time.time() | |
print("list_all_children") | |
children = list_all_children(members) | |
e = time.time() | |
num = len(children) | |
print num | |
print e-s | |
s = time.time() | |
print("list_all_children4") | |
children = list_all_children4(members) | |
e = time.time() | |
num = len(children) | |
print num | |
print e-s | |
s = time.time() | |
print("list_all_children5") | |
children = list_all_children5(members) | |
e = time.time() | |
num = len(children) | |
print num | |
print e-s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment