03 - selecting by attribute name, filtering by type
Selecting by attribute name
Leading on from the previous section, someone asked:
Is there an easier way to choose everything in the scene that has a .radius attribute?
There is indeed; a semi-hidden feature of the ls command is that if you give it an attribute name, it'll return a list of objects with that attribute. Eg, I have 4 spheres with construction history, and a phx area light:
from pymel.core import *
ls('*.radius')
# Result: [Attribute(u'polySphere3.radius'),
Attribute(u'polySphere2.radius'),
Attribute(u'polySphere1.radius'),
Attribute(u'lgt_asdfShape.radius'),
Attribute(u'lgt_asdfShape.radius')] #
from pymel.core import *
ls('*.radius')
# Result: [Attribute(u'polySphere3.radius'),
Attribute(u'polySphere2.radius'),
Attribute(u'polySphere1.radius'),
Attribute(u'lgt_asdfShape.radius'),
Attribute(u'lgt_asdfShape.radius')] #
From that, its easy to set all the radiuses (radii? radium?) at once. Note that the above list is a direct link to the attribute itself, so we can just go x.set(5) directly.
[ x.set(5) for x in ls('*.radius') if x ]
[ x.set(5) for x in ls('*.radius') if x ]
Filtering by type
Which led to the next question:
Ah, but that might select the wrong things! Can we limit the selection by type?
You sure can. Using the above example as a guide, we need to do a few things:
Get from the attribute name to the object type
Test the object type to see if its what we want
Set the attribute value
So working with the same mix of spheres and lights, lets inspect the first object we find with the radius attribute:
foo = ls('*.radius')[0] # grab the first object in the list
print foo
# Result: polySphere3.radius
foo = ls('*.radius')[0] # grab the first object in the list
print foo
# Result: polySphere3.radius
Lets query the type of this thing:
print foo.type()
# Result: float
print foo.type()
# Result: float
Hmm, this is giving us the type of the attribute, not the type of the object. We could take the foo string, split it at the fullstop, get the first part, and query its type, but Luke Harris pointed out a much easier way; use nodeType(). It's smart enough to go directly to the node rather than the attribute:
print foo.nodeType()
# Result: polySphere
print foo.nodeType()
# Result: polySphere
The type comes back as a string. Now we want a True/False value if it matches the type we want. One way to do this is 'thing_to_find' in mystring. Eg:
blah = foo.nodeType()
'polySphere' in blah
# Result: True #
blah = foo.nodeType()
'polySphere' in blah
# Result: True #
or if we want the opposite, throw a 'NOT' in there:
'polySphere' not in blah
# Result: False #
'polySphere' not in blah
# Result: False #
Right, lets throw this all together. In english, we'll say
Set the radius attribute to 4 for all objects with a radius attribute, if the object type isn't a polysphere.
It looks complicated, but look closer, you can see its made up of all the little blocks we just worked out:
[ x.set(4) for x in ls('*.radius') if 'polySphere' not in x.nodeType() ]
[ x.set(4) for x in ls('*.radius') if 'polySphere' not in x.nodeType() ]
Strictly speaking you'd do the opposite, and only run on phxAreaLight nodes for example (the 'not' is a bit messy) :
[ x.set(4) for x in ls('*.radius') if 'phxAreaLight' in x.nodeType() ]
[ x.set(4) for x in ls('*.radius') if 'phxAreaLight' in x.nodeType() ]