We've Moved


The blog has been retired - it's up for legacy reasons, but these days I'm blogging at blog.theodox.com. All of the content from this site has been replicated there, and that's where all of the new content will be posted. The new feed is here . I'm experimenting with crossposting from the live site, but if you want to keep up to date use blog.theodox.com or just theodox.com

Thursday, June 26, 2014

Binary bonanza!

Here's a very complete source for a variety of python package binary distributions, covering a pretty broad variety of platforms, python versions and bit depths.  A good page to bookmark next time your scrounging for a rare extension module you can't compile.

Tuesday, June 24, 2014

mGui updates

For anybody whos been following the mGui Maya GUI construction kit posts, I've added a few fixes and tweaks to the GitHub project in the last couple of weeks:


mGui.progress

The progress module wraps Maya's progressBar command for mGui style coding of progress bars.

There are two classes in the module;  ProgressBar  is the generic version and MainProgressBar always points at Maya's main progress bar.  Both classes have start(), update() and end() methods instead of Maya's clunky cmds.progressBar(name, e=True, beginProgress=1)  and so on.  They also both have an iter method, which will loop over a generator expression and update the progress bar for each yield then pass along the value. This allows simple idioms like:


from mGui.progress import MainProgressBar
import os

def list_files(dir):
    # pretend this is as long, slow function...
    for item in os.listdir(dir):
        yield item

pb = MainProgressBar()
for each_file in pb.iter(list_files()):
    print each_file.upper()
    # here's where you do something with the results


So you can update the progress bar without unduly intertwining the GUI update and the program logic.

mGui.menu_loader

The menu_loader module will create menus from a YAML data file.  It does a little bit of introspection to figure out how to create the items and attach their handlers to functions. This makes it easy to set up a menu with several items from a single setup routine.

The menu data is a text-based YAML file that looks like this:


!MMenu
    key:   UndeadLabs
    label: Undead Labs
    items:
        - !MMenuItem
            key: About
            label: About Undead Labs...
            annotation: "About this UndeadLabs tool setup"
            command: tools.common.aboutDialog.open

        - !MMenuItem
            key: RemoteDebugger
            label:  Remote Debugger
            annotation: Start / Stop remote python debugger
            command: tools.common.remoteDebug.remote_debugger_dialog


And loading the menu is as simple as:


import mGui.menu_loader as loader
loader.load_menu('path/to/undeadlabs.YAML')


mGui.scriptJobs

The scriptJobs module adapts the event model for use with scriptJobs. A ScriptJobEvent is a derivative of Event which allows you to hook multiple handlers to a single scriptjob (in the same way that the other Event classes allow for multicast delegates):

from mGui.scriptJobs import *

def print_selected (*args, **kwargs):
    print cmds.ls(sl=True) or []

sj = ScriptJobEvent("e", "SelectionChanged")
sj += print_selected

As with all the mGui Event classes, you can add multiple handlers to  single event:

sj += some_other_function()

The module also includes named subclasses to simplify setup. That way you can do things like:
closing_sj = RecentCommandChanged()
closing_sj += close_handler

which is a bit nicer and less typo prone if you use an autocompleting IDE.



Friday, June 20, 2014

From the annals of bug subtlety

... comes an object lesson in why it's nice to have a debugger.

I'm porting a biggish python codebase to support multiple OSs  and maya versions.  As I move things around I try to use the opportunity to shore up test coverage.  And it feels like the most boring chore imaginable, until something like this crops up.

I've got an exporter framework that I share between projects and files, and when I was moving from 2011- only to multiple version.  It's important code, so it's tested - but the test is really dumb: it creates a test scene, imports the test framework, and calls one function. But running the tests in 2014 never works - even though I can manually execute the exact steps in a regular copy of maya and all is well.

So I threw it under the debugger -- PyCharm FTW!  -- and started stepping through. No dice, everything seemed OK but still the test failed: it could not find my test objects. Finally, in desperation, I started stepping though the test and issuing an ls() after every step... and I found that the break wasn't caused by running code - it was caused by importing my module.  I didn't call it - just imported it.  WTF?

It turns out that importing PyMel was wiping my test scene in 2014! The tests all run under maya.standalone, and the bug only shows up there, which is why just doing it by hand in maya wasn't showing the same symptoms.

Here's my repro case:

import maya.cmds as cmds
cmds.polyCube()
# [u'pCube1', u'polyCube1']
cmds.ls(type='transform')
# [u'front', u'pCube1', u'persp', u'side', u'top']
import pymel.core as pm
cmds.ls(type='transform')
# [u'front', u'persp', u'side', u'top']

This is a 100% repro in maya.standalone - but not in GUI maya, where the bug does not occur.Is this true for everybody else ?   The workaround is to import pymel earlier so that the destruction doesn't affect anything important. 

But... ouch!

Friday, June 6, 2014

Chromosaurus!

This 1985 short from Pacific Data Images (the X chromosome in the DNA of Dreamworks Animation) was extremely popular in the late 80's / early 90's CG compilation VHS market, frequently accompanied by trippy electronica.



Of special note: this is well before the the popularization of our mesh skinning techniques: all the moving parts are jointed, and the ripples in the tail are all damped sine waves instead of skeletal animation.  I'm pretty sure these aren't raytraced, it looks to me like a tweaked reflection map.

One of the saddest generation gap experiences of my life is trying to explain to my 14 year old son, who's never known a world without casually photoreal CG, why this set a generation of nerdly hearts afire.  At least he appreciated the soundtrack.

Tuesday, June 3, 2014

Save the environment!

+Rob Galanakis  posted this on Google+, and as I started to reply I realized this would be a useful thing to put out to a wider audience.  Or maybe useful isn't the right word - it's just something that bugs me so I want to bloviate about it.
Hey +Cory Mogk , +Brad Clark, +Steve Theodore , and whoever else in #techart . Has anyone figured out a viable model for reusing Python libraries in Maya, like other Python applications with pip/virtualenv/requirements.txt do? Is there a conversation anywhere?
The short answer to Rob's question is : I've tried to go this route, but I couldn't get a virtualenv-based approach to match for my needs.   For the long answer, read on...