Python

Maya Python: Reset Selected Controls

by Chris Lesage on October 1, 2013

Reset Selected Script

I have a script snippet to share with you today. This is how I quickly reset all selected objects to 0,0,0,0,0,0,1,1,1 in translate, rotate and scale with the stroke of a hotkey command.

This is fantastically useful when you are animating! But it’s also for riggers, modellers or anyone working in Maya. Softimage XSI has this feature built in (Ctrl-Shift-R), so I made a little snippet to do the same in Maya.

First I’ll show the simple code, explicitly written for each attribute. And then for the Python fans, I’ll show how to shorten it using List Comprehension because why not? After the scripts, I’ll explain a bit more how it works. If you just want the script, just copy/paste this and use it. If you want to geek out about List Comprehension and PyMEL, read on…

Reset Selected: The simple version

import pymel.core as pm

def reset_selected(oColl):
    for oNode in oColl:
        try: oNode.tx.set(0)
        except: pass
        try: oNode.ty.set(0)
        except: pass
        try: oNode.tz.set(0)
        except: pass

        try: oNode.rx.set(0)
        except: pass
        try: oNode.ry.set(0)
        except: pass
        try: oNode.rz.set(0)
        except: pass

        try: oNode.sx.set(1)
        except: pass
        try: oNode.sy.set(1)
        except: pass
        try: oNode.sz.set(1)
        except: pass

runthescipt = pm.Callback(reset_selected, pm.selected() )
runthescipt()

Reset Selected: The Fancy List Comprehension Version

import pymel.core as pm

def reset_selected(oColl):
    trList = ['.tx','.ty','.tz','.rx','.ry','.rz']
    sList = ['.sx','.sy','.sz']

    # o is each object, x is each attribute
    for attr in [(o, x) for o in oColl for x in trList]:
        try: pm.Attribute(attr[0] + attr[1]).set(0)
        except: pass
    for attr in [(o, x) for o in oColl for x in sList]:
        try: pm.Attribute(attr[0] + attr[1]).set(1)
        except: pass

runthescipt = pm.Callback(reset_selected, pm.selected() )
runthescipt()

Notes about this script:

  • Fully supports undo and redo.
  • There are 2 scripts below. Grab either one. They do the same thing.
  • Set it to Ctrl-Shift-R in your Maya hotkey editor. Don’t forget to set the hotkey to Python.
  • I personally use PyMEL instead of maya.cmds because it can generate a list of object pointers rather than strings, so it is a lot easier to use from an OOP point of view. Some studios do not use PyMEL, so be aware of this.
  • The pm.Callback() takes an argument, so instead of pm.selected() you could pass it a list of all your character rig controls, or all the locators in your scene, etc. etc.
  • Every iteration has a try/except on it, so that if your attribute is driven or locked the script will continue without failing.
  • You can also use makeIdentity() to quickly reset an object, but if some of your attributes are locked, this won’t work.

How list comprehension works:

  • List Comprehension lets you build up lists quickly by using [x for x in y]
  • But you can also loop over two lists at once with one line:
    [(x + y) for x in (1,2,3) y in (4,5,6)]
  • This acts like a nested iterator which will add each of (1,2,3) with each of (4,5,6) resulting in 9 combinations. (1+4, 1+5, 1+6, 2+4, 2+5, and so on…)
  • I then take those two combined elements to gather all the transform attributes for all of my objects.

How pm.Attribute works:

  • I’m iterating over two lists with my list comprehension so I need to combine it again, casting it as a pm.Attribute( object name + attribute ).
  • However, if you were explicitly setting an attribute it would be as simple as yourObject.tx.set(0)
  • You could also create your attribute in the list comprehension, but if you try and run the script on a shape node that has no attributes, then the script will fail.
  • You could get around this by including a try/except function inside the list comprehension or by filtering your objects for transform nodes only, but that is just becoming too complex for this little script.

In Summary

I use this hotkey all the time! I missed it from Softimage. Your animators are going to love using it too! Feel free to use it, and if you find bugs, improve it or rewrite it in MEL or maya.cmds let us know in the comments. Thanks!

Python: String Formatting and Enumerate

by Chris Lesage on February 21, 2012

As a mostly self-taught Python scripter, I try to keep on top of best-practices and constantly learn, because I realize how easily bad habits can slip in. I’ve recently learned these bad habits are known as anti-patterns.

That said, I learned a couple new Python tricks this week.

1. Better String Formatting Using Format()

If you’ve used string formatting before, this is very similar, except the replacement fields are inside {} brackets, and you can use variables as keywords or an index.

examples:

print 'We are the {0} who say "{1}!"'.format('knights', 'Ni')
print 'We are the {1} who say "{0}!"'.format('Ni', 'knights')
print 'We are the {people} who say "{quote}!"'.format(people='knights', quote='Ni')

There are also lots of examples of how to format numbers or create precise column spacing. The “old string formatting” is eventually going to be removed from Python. There is likely no rush to switch, but it is less flexible and a bit harder to read:

print 'We are the %s who say %s!' % ('knights', 'Ni')

source: http://docs.python.org/tutorial/inputoutput.html

2. Looping With Enumerate()

One of the common things you want when looping through things is an index counter. You can do this a few various ways:
counter += 1
for i in range():
or you can use each.index() on array items. There is a better way:

Enumerate() is nice PEP-friendly way to get a “a compact, readable, reliable index notation” in loops. It works even when you don’t have an array to count, or a known length (for doing a range()). You use two variables in your for loop, and the first is your index. It is much nicer than having a separate counter or measuring the length of something which may change inside the loop.

(This is a PyMEL example in Maya and assumes you have a selection of objects.)

import pymel.core as pm

for i, each in enumerate(pm.selected()):
    print i, each

for i, each in enumerate(['some', 'array', 'here']):
    print i, each

3. Put Them Together

Let’s do a simple example where we also use .format() to rename a series of objects. I’ll use {1:02d} to automatically add frame-padding. {1:__} is the replacement field index. {__:02d} is the frame-padding. (You can also convert to percentages, hexadecimal, dates, change the column spacing, etc. etc. This is just one small example.)

for i, each in enumerate(pm.selected()):
    newName = '{0}_{1:02d}'.format('right_whisker', i)
    pm.rename(each, newName)

The nice thing about .format() is how easy it is to read and edit, since the replacement fields are clearly separated by brackets and you can pass any variables to them. Again this isn’t new, but it is easier to read than the old string formatting. So let’s play with this a bit and expand the example to automatically rename the object as “left” or “right”, depending on its translation in X.

for i, each in enumerate(pm.selected()):
    if each.tx.get() > 0:
        side = 'left'
    else:
        side = 'right'
    newName = '{0}_{1}_{2:02d}'.format(side, 'whisker', i)
    pm.rename(each, newName)

In the past, one of the most common ways I’ve created an index count is by measuring a length of a list or array or by using .index() to return the index of an array item. In comparison, this looks very ugly now. Plus, if you were to delete some objects inside the loop, the length of your range might change.

objects = pm.selected()
for i in range(0,len(objects)):
    print i, objects[i]

Two useful new tools for the arsenal. If you have any tips, suggestions or improvements, leave a comment. Next up, I’ll be getting back to the Mini Mammoth rigging after a bit of a hiatus.