Due to a miscommunication between Elsevier Press and the Python Software Foundation, the first printing of this title used an unauthorized modification of the trademarked Python logo. We apologize to the PSF for this, and they have been understanding of our in-press status; in the 2nd and subsequent printing of this title, we shall use a cover design that has been approved as non-dilutive by the Python Software Foundation.

Help make the world a better place and make a secure donation to the Python Software Foundation today!


Post-GDC Wrap-Up

GDC has passed and I’ve started to recover from the workload that was awaiting me upon my return, so I thought I’d toss up a few points that came up. A lot of stuff I talked about was related to topics that can be found in the book, but I thought I’d share a few notes on other stuff. Please feel free to ask questions or to disagree in the comments for this post!

  • I suggested that idiomatic Python is not simply a matter of using OOP (hence, using pymel, although awesome, is not sufficient to make your code “Pythonic”). I stressed that the Python community is made up of a variety of programming cultures that each have their own particular design patterns and paradigms (be it procedural, functional, object-oriented, or some half-baked convention of web programmers), and that the few existing unifying factors are centered more around readability and testability of code.
  • I talked a bit about code organization and Python’s data model in contrast to MEL (stressing again that MEL and the cmds module are equal citizens with respect to the Command Engine). I used function pointer arguments for Maya commands as one example where the differences between Python and MEL become clearer when organizing code. Namely, I would argue that there is almost never a good reason to pass a command string rather than a function pointer to e.g., GUI commands, scriptJob, etc. Passing command strings a) is more prone to error since you won’t get code completion in a string literal, b) will execute statements in __main__ rather than in the calling context, and c) therefore throws exceptions when the callback is issued and without a stack trace, rather than when the command is executed and with a stack trace.
  • I also related points about Python’s data model to the rationale for MScriptUtil, and pointed out simple ways to subclass parts of the API that make use of it in order to create more “Pythonic” APIs, as in the following snippet.
  • import collections
    import maya.OpenMaya as om
    UV = collections.namedtuple('UV', ('u','v'))
    class MFnNurbsSurfaceX(om.MFnNurbsSurface):
        def __init__(self, *args):
            om.MFnNurbsSurface.__init__(self, *args)
        def closestPoint(self, *args):
            uutil = om.MScriptUtil()
            uptr = uutil.asDoublePtr()
            vutil = om.MScriptUtil()
            vptr = vutil.asDoublePtr()
            pt = om.MFnNurbsSurface.closestPoint(self, args[0], uptr, vptr)
            return pt, UV(uutil.getDouble(uptr), vutil.getDouble(vptr))
  • One final novel point I mentioned was a technique for plug-in deployment within a package in order to streamline tool distribution and access plug-in classes from corresponding GUI tools (and enable users to simply import a single plug-in module rather than calling the loadPlugin command for several files if so desired). A brief example follows.
import os
import maya.cmds as cmds
import maya.OpenMaya as om
## the Maya plug-in path environment variable
kPluginPathEnvVar = 'MAYA_PLUG_IN_PATH'
def appendToPluginPath():
    Modify the Maya plug-in path for this session to include this module.
    path = os.environ[kPluginPathEnvVar]
    folder = os.path.dirname(__file__)
    if not folder in path:
        path = os.pathsep.join([path, folder])
    os.environ[kPluginPathEnvVar] = path
def getPluginFileName():
    Get the plug-in file name for use with the loadPlugin command.
    return '%s.py'%os.path.splitext(os.path.basename(__file__))[0]
# import classes from modules
import classes.pluginOne
import classes.pluginTwo
# etc.
def initializePlugin(mobject):
  # create fn, register plugins
def uninitializePlugin(mobject):
  # create fn, deregister plugins
# following lines enable ability to "import" plug-ins via this module
# __file__ doesn't exist in context where loadPlugin evaluates
except NameError: pass

Last but not least, Ryan recently completed a MasterClass for Autodesk on vector math for artists, where he provides some examples using Python. You can check it out on Autodesk’s Area web site.

~ by Adam on March 19, 2012.

Leave a Reply

Your email address will not be published. Required fields are marked *