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() uutil.createFromDouble(0) uptr = uutil.asDoublePtr() vutil = om.MScriptUtil() vutil.createFromDouble(0) vptr = vutil.asDoublePtr() pt = om.MFnNurbsSurface.closestPoint(self, args, uptr, vptr) return pt, UV(uutil.getDouble(uptr), vutil.getDouble(vptr))
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__)) # import classes from modules import classes.pluginOne import classes.pluginTwo # etc. def initializePlugin(mobject): # create fn, register plugins return def uninitializePlugin(mobject): # create fn, deregister plugins return # following lines enable ability to "import" plug-ins via this module try: appendToPluginPath() cmds.loadPlugin(getPluginFileName()) # __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.