Sunday, July 27, 2014

Pythonistas need Pythonista!

If you consider yourself a Pythonista, you've probably been frustrated by the difficulty involved in getting to work in Python on iOS devices.  I just stumbled upon a really cool answer to your prayers in the form of Pythonista. It's not brand new - it looks like it came out last year - but I just found out about it and flipped my proverbial wig.

Pythonista is a sandboxed Python 2.7 development environment for iOS.  It borrows a page from the playbook of earlier sandboxes like Codea. and manages to skirt Apple's rules for what you can do on the device while still allowing plenty of power.  It includes a script editor (a pretty slick one for iOS, by the way) ,an interactive environment, and a bunch of libraries to make development really useful.  Among the 'batteries' included are heavy hitters like pil, numpy and matplotlib, along with a few cool little things like a text-to-speech module and tools for dealing with the iOS console.

The most impressive inclusions are the scene and ui modules: custom modules devoted to iOS drawing and UI.  Ironically, it's easier to develop a GUI application on your iPad using Pythonista than it is to do it on a desktop machine - the app even comes with a UI builder tool similar to QT's interface builder (not nearly as deep or complex, of course, but iOS UI is less complex than desktop). You can read multiple touches.  You can even do hardware accelerate drawing - nice for things like a finger-sketching program.  Since Pythonista includes pil, you can even do stuff like image processing:



Pythonista's main limitation is that it's not possible to add external modules to the library in the usual ways: setuptools and pip aren't available.  You can manually install pure-python modules by copy-paste-save, and there are few installation tools floating around on the web such as pipista and Pypi.  (As an aside: here's a handy collection of Pythonista snippets and links).  Modules with binary dependencies -- such as the perforce api -- are off-limits; I'm not sure it it would be possible to use .pyd's that were properly compiled for iOS or if the security sandbox won't allow arbitrary binary code at all.

All in all, it's pretty cool stuff for any Pythonerd.  My big project right now is a touch based inteface on the iPad to control a BrickPi Mindstorms robot, but at some point I think an asset-database / issue tracker client on the iPad would be a handy tool for our production team .  Pretty cool for $6.99!

Friday, July 11, 2014

Pipeline book out in Japan (!)

I just heard that the Production Pipeline book is out in Japan as of today. Wowsers!

This is actually my second time being published in Japan. I also contributed a chapter to 97 Things Every Game Creator Should Know.  But still. Japan. I mean...  

PS:  The books is still up for sale on Amazon: And it's also on the Tech Art Bookstore page.

Wednesday, July 9, 2014

Handy link: Python string format cookbook

If you're like me and addicted to using the old-school percent-symbol based string format, the newer bracket-based formatting is probably a bit mysterious. The python docs certainly don't help, they seem to be written for C programmers on meth (a help document entitled 'string format specification mini language' does not scream 'usability' to me, at any rate).

So, many props to Marcus Kazmierczak for his handy Python String Formatting Cookbook page. It's already saved me a ton of profanity. Here's to elevating the discourse of the internet!

Saturday, July 5, 2014

Save The Environment 2: I am the .Egg Man

In my last, bumper-sticker-laden post I offered to share a little bit about the way I pack up my tools for users.  This time I'll try to actually describe the process.

After a all the buildup, I wish I could make this sound more high tech and impressive. Basically, I just pack up what I have and send it all out to my users in a big ol' zip file.  The zip ends up on their Maya's PYTHONPATH, and gives them exactly the same stuff I had when I created the zip.  That's kind of it.  It's basically a simplified version of a python egg; however since I'm distributing an entire ecosystem in one shot I've opted to do the packaging myself instead of relying on setuptools and all of its complex dependency management arrangements.

Simple as it is, this system has saved me a huge amount of time and energy over the last few years. It's been a long time since I've had to worry about a mysterious import failure or the wrong version of a module.  Simplicity and robustness are very important, especially in the foundation of a pipeline. Of course, they don't always make for the most engaging blog posts  But I'll do what I can, even if it means resorting to some pretty lame egg puns.

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!