Skip to content

02 - relatives of objects, history of objects

Array elements, or why is [0] everywhere?

Before moving on, a little bit of python theory. In the last section I mention how to create an array or list of things:

python
mylist = ['maya','xsi','max','houdini']

print mylist
# ['maya', 'xsi', 'max', 'houdini']
mylist = ['maya','xsi','max','houdini']

print mylist
# ['maya', 'xsi', 'max', 'houdini']

To get just one element from that list, append the position of that thing in square brackets, with the first position being 0:

python
print mylist[0]
# maya

print mylist[3]
# houdini
print mylist[0]
# maya

print mylist[3]
# houdini

Often when working with pymel, you'll ask maya for some objects, and it will nearly always return a list. Even if that list contains only 1 thing, it'll be in a list.

If you try and pass that thing to another command, it'll get confused. Why? Because that other command expects a single thing, not a list. If you ever get this problem, the solution nearly every time is to append [0]. Ok, moving on....

listRelatives

The previous section assumes that the objects you're selecting contain the attributes you want to change. But what if that's not true? What if you want to change something which is an input to your selection?

For example, you run the create -> polygon primitves -> sphere command twice. You'll end up with 2 spheres, each connected to their own polysphere node. If you select the transform, how do you change the radius?

This implies 2 questions:

   given a transform, how do you get to the shape   given a shape, how do you get to its construction history

The 'listRelatives' command will list any child shapes for a transform. It can do more than that, but when called by itself, does what we need.

python
from pymel.core import *
selection = ls(selection=True)
selection =  selection[0]     # lets just deal with one sphere for now

print 'the transform is: ',
print selection

"the transform is:  pSphere2"

myshape = selection.listRelatives()
print 'the shape is: ',
print myshape[0]

"the shape is:  pSphereShape2"
from pymel.core import *
selection = ls(selection=True)
selection =  selection[0]     # lets just deal with one sphere for now

print 'the transform is: ',
print selection

"the transform is:  pSphere2"

myshape = selection.listRelatives()
print 'the shape is: ',
print myshape[0]

"the shape is:  pSphereShape2"

the 'listHistory' command lets you see all the inputs to a node. So in this case if we give it one of the shapes, it should return the polysphere command:

python
myhistory = myshape[0].listHistory()
print 'the history is: ',
print myhistory

"the history is:  [nt.Mesh(u'pSphereShape2'), nt.PolySphere(u'polySphere2')]"
myhistory = myshape[0].listHistory()
print 'the history is: ',
print myhistory

"the history is:  [nt.Mesh(u'pSphereShape2'), nt.PolySphere(u'polySphere2')]"

And once we have that, we can then get to the radius attribute of the polysphere command and change it:

python
mypolysphere = myhistory[1]
mypolysphere.radius.set(5)
mypolysphere = myhistory[1]
mypolysphere.radius.set(5)

so, if we wanted this to happen across lots of spheres at once, and list comprehend the shit outta this, we could do this:

python
selection = ls(selection=True)
shapes = [ x.listRelatives()[0] for x in selection ]
spheres = [ x.listHistory()[1] for x in shapes ]
[ x.radius.set(0.5) for x in spheres]
selection = ls(selection=True)
shapes = [ x.listRelatives()[0] for x in selection ]
spheres = [ x.listHistory()[1] for x in shapes ]
[ x.radius.set(0.5) for x in spheres]

Or as a horrible one-liner (which is actually what I'd do cos I'm lazy)

python
[ x.listRelatives()[0].listHistory()[1].radius.set(2) for x in ls(selection=True)]
[ x.listRelatives()[0].listHistory()[1].radius.set(2) for x in ls(selection=True)]

But wait! We can cheat even further!

I found out that listHistory doesn't need a shape fed into it. Give it a transform, and it will work out the shape first, then the history for the shape. Means you can skip a step:

python
[ x.listHistory()[1].radius.set(1) for x in ls(selection=True)]
[ x.listHistory()[1].radius.set(1) for x in ls(selection=True)]

todo: given a construction history object, how do you get to the transform?