Posts Tagged ‘ Maya

Visual guide to Tkinter widgets

Over on my Python Wiki, I’ve started making a ‘visual guide to Tkinter widgets’.  I’ve found a lot of good online Tkinter reference, but nothing with pictures… so I figured I’ll make my own :)  I find this to be a good exercise when first learning a new UI system, plus it makes for a nice quick-reference when authoring them later.

As an asside, I’ve also started this process for Maya’s Extended Layer Format (ELF) UI system on my Mel Wiki HERE.

See the growing Tkinter widget list HERE, but a few examples are pasted in below:

etc…

Understading Python generators in Maya

Python has the ability to create generator functions. See Python docs here:
http://docs.python.org/tutorial/classes.html#generators
http://docs.python.org/reference/expressions.html#generator-expressions
http://docs.python.org/reference/expressions.html#yieldexpr
http://docs.python.org/reference/simple_stmts.html#yield

What are they? From the Python docs (above):

“Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. Each time next() is called, the generator resumes where it left-off (it remembers all the data values and which statement was last executed)…”

You can imagine that they are like a normal function looping over a list of items, or doing some work on each of the items in the list. The difference is, rather than looping over the whole list when the function is executed, the function pauses after each loop, waiting for you to tell it to continue.

This allows you the ability to make tools that can execute over a list of items, only executing on the next item when you tell it too.

Example below. In this example, the user writes a generator function that will create a locator placed at each vert passed into the function. But the locators are created one by one, only when we call to the generator function:

# Python code
import maya.cmds as mc

# Define our generator function
def generateLocs(verts):
    for v in verts:
        pos = mc.pointPosition(v, world=True)
        loc = mc.spaceLocator(position=pos)
        # Use 'yield' instead of 'return':
        yield loc[0]

Now put the code to use:

# First, select a polygonal object.  Then convert to verts:
verts = mc.ls(mc.polyListComponentConversion(toVertex=True), flatten=True)

# create our generator object called "loc":
loc = generateLocs(verts)

# each time we call to loc.next(), a new locator is created, base on
#  the next vert in the list:
print loc.next()
# locator1
print loc.next()
# locator2
print loc.next()
# locator3

# etc...

This post is also over on my Mel Wiki

Casting variable data from Python to Maya

I’ve recently ran across a nasty… bug, in the Maya-Python integration.  It appears, that if you have a Python module return a list, Maya will convert it to an array. That’s as expected.  However, if the Python list is empty, Maya converts it to a string with two brackets inside of it: “[]”, rather than converting it to an empty array.

I should note, as an update, that this initial presumption (that this is a ‘bug’) is wrong:  Maya variables are always typed (string $foo = “word”;), while Python’s aren’t, they’re just pointers (foo = “word”).  Maya has to auto-detect what type of data (string, float, etc) exists in the passed-in Python object and make a determination for what the conversion should be.  If it just gets an ’empty list’, it really has no idea what type of Maya array (float, string, int, vector) it should be.

Obviously, this can cause some real headaches if you’re trying to capture expected data from Python in Maya.

I found a fix, that I’m… not…. very… happy with.  Explained below the example.

Example:  First, a Python module that will return different types of data, based on the passed in arg:

# Python code
def variableReturn(varType):
    if varType == "str":
        return "string"
    if varType == "listSingle":
        return ["value1"]
    if varType == "listMult":
        return ["value1", "value2"]
    if varType == "listNone":
        return [None]
    if varType == "listEmpty":
        return []

Now, some mel code to capture that data.  I’ve already setup the Maya variable types to be the correct ones based on what the Python module spits out… but you can see that one of them is clearly wrong:

// Mel code:
string $str = python("variableReturn('str')");
string $lstSingle[] = python("variableReturn('listSingle')");
string $listMult[] = python("variableReturn('listMult')");
string $listNone[] = python("variableReturn('listNone')");
string $listEmpty = python("variableReturn('listEmpty')");

print ("Type of 'str' : " + `whatIs "$str"` + "n");
print ("Type of 'listSingle' : " + `whatIs "$lstSingle"` + "n");
print ("Type or 'listMult' : " + `whatIs "$listMult"` + "n");
print ("Type of 'listNone' : " + `whatIs "$listNone"` +"n");
print ("Type of 'listEmpty' : " + `whatIs "$listEmpty"` + "  <--- aaaagh!!!n");

Prints:

Type of 'str' : string variable
Type of 'listSingle' : string[] variable
Type or 'listMult' : string[] variable
Type of 'listNone' : string[] variable
Type of 'listEmpty' : string variable  <--- aaaagh!!!

As you can see, the workaround is to have your Python code return a list with a ‘None’ object in it:

# Python
return [None]

Presumably, you’d do some sort of test before hand in your Python code to know to return things this way:

# Python
if len(myVal) == 0:
    myVal = [None]
return myVal

Of course, in Maya you’d then have to detect if the first item of the list was empty, if it’s size was 1:

// mel
string $result[] = python("myPyFunc()");
if(size($result) && $result[0] != ""){
    // start doing something...
}

Update #1:

After reading through the Maya docs on Python integration, this may make a bit of sense:

Python Return Value MEL Conversion
string string
unicode string
int int
float float
list containing numbers, including at least one float float[]
list containing only integers or longs int[]
list containing a non-number string[]
anything else string

I’m guessing an ’empty list’ falls into the ‘anything else’ category, and thus converts it to a “string”. Even though our above example returns an empty list, and we presume to have string data, it could actually contain float data, int data, etc. But how is Maya to know this if all it gets is an un-typed empty list? It doesn’t, so it turns it into a string.

Update #2:

Looks like (based on comment below) later version of Maya have fixed this issue:  The docs (in 2010) now append this new information:

| empty Python list | empty string array (string $array[]) |

Centering joints in edge loops

When I’m rigging, I will often create an initial ‘rig-skeleton’ that fits inside the mesh to be deformed. To speed things along, I first just rough in the joint positions by snapping them to verts.   But later I like to get them centered into a selection of verts, and usually that selection is some form of edge loop.

I wrote some mel that does this:

  • You pick a joint, and an edge on a mesh
  • The code detects the joint, and the edge
  • Based on the edge, find the edge loop
  • Convert the edge loop into a selection of verts
  • Find the average position of all the verts
  • Move the joint pivots to this new position
  • Reselect the joint, and the new vert selection.

You can find the mel code over on my Mel Wiki.

Auto Joint Orientations in Maya

I have this pet peeve:  Whenever I create joint hierarchies in Maya, it will automatically setup the joint orientation value for you (based on option set in the ‘Joint Tool’ tool settings).  That, is handy.  However, the last joint in the chain isn’t auto-oriented:   A joint’s auto-orientation is based on the vector between it, and its child.  So obviously, the last joint in the chain has no child, so it can’t use this system.  The result is, the last joint is always oriented to the worldspace global axis.  Which is almost exactly never what I want.

The workaround is easy, just make a little script to process all the joints in a selected hierarchy.  But it sure would be nice if they had an option to do this automatically in the UI….

"""
Eric Pavey - 2008-12-19
For the selected hierarchies, zero the joint orient values
on all leaf joints
"""
# Python code
import maya.cmds as mc

kids = mc.listRelatives(mc.ls(selection=True), children=True, type="joint", allDescendents=True)
for k in kids:
    if mc.listRelatives(k, children=True, type="joint") is None:
        for attr in [".jointOrientX", ".jointOrientY", ".jointOrientZ"]:
            mc.setAttr(k+attr, 0)
        print "Zeroed '" + k + ".jointOrient'"