Skip to content

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:

python
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.

python
[ 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:

python
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:

python
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:

python
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:

python
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:

python
'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:

python
[ 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) :

python
[ 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() ]