Tips and tricks for scripters new to Python

 
 
 

Built-in Python libraries

Python has many useful built-in libraries and data structures. In most cases, there is an existing library that can be used for general programming tasks, which obviates the need to create custom utilities as in the case of MEL scripts. Documentation of the built-in Python libraries can be found at http://docs.python.org. This site also provides a good introductory tutorial for general Python programming.

Custom UI using a Python class

One handy Python function is functools.partial, which can be used to pass extra information to a callback function. For example, the following class creates a window with several buttons that print a number when clicked on:

from functools import partial
import maya.cmds as cmds
class ButtonWin(object):
   def __init__(self):
      self.win = cmds.window()
      self.layout = cmds.columnLayout(parent=self.win)
      for x in range(10):
         cmds.button(label="Click Here %d"%x, parent=self.layout,          command=partial(self.report,x))
         cmds.showWindow()
   def report(self,buttonIndex,value):
      print "button %d got %s"%(buttonIndex,value)
f = ButtonWin()

Debugging Python scripts in Maya

One advantage of using Python scripts is the availability of debuggers for use during the development process. This can save a lot of time when troubleshooting a scripting problem.

With pdb

Python comes with a built-in debugger in the pdb module. pdb has a text interface which is adequate for quick debugging sessions. If pdb is used in a script which is invoked from the Script Editor, an input dialog box appears whenever pdb needs input. Using pdb is as simple as importing it, and then setting a breakpoint as follows:

import pdb
pdb.set_trace()

When execution reaches the second line, the input dialog appears and you can use standard commands to continue, step-into, print values, and so forth.

Full documentation for pdb can be found on http://docs.python.org/library/pdb.html.

With an external debugger

For complicated debugging tasks, it is probably easier to use an external graphical debugger. There are several free and commercial IDEs, such as:

External debuggers are used by first importing a utility module inside Maya, which opens a communication socket to the IDE. Once the IDE is connected to Maya, you can open your script files to set breakpoints, inspect variable values and so on. Each IDE will have its own required workflow; for details, consult its documentation.

Using Wing IDE with Maya

To start a debugging session of a Maya Python script with Wing IDE 3.1 in Windows, do as follows:

  1. Copy wingdbstub.py from the Wing IDE installation directory into the Maya Python script path. For example, copy C:\Program Files (x86)\Wing IDE 3.1\wingdbstub.py to C:\Users\yourname\Documents\maya\scripts
  2. Ensure that Enable Passive Listen is checked in Wing IDE > Edit > Preferences > Debugger to allow connection from external processes. Windows may pop up a dialog box asking for authorization to open a communication port. You should allow this.
  3. Start Maya, and ensure your Python script can be imported successfully and run.
  4. In Wing IDE, open up the same script. Set a breakpoint using F9 or by clicking in the leftmost margin.
  5. In Maya, execute the Python statement
    import wingdbstub
    

    Windows may pop up another authorization dialog box asking for authorization. You should allow this.

  6. Check back in Wing IDE; the status indicator in the lower left of the main Wing IDE window should be green. This indicates that the remote debugging connection has been established, and that Maya is running.
  7. In Maya, call the Python function that contains your breakpoint. When that breakpoint is reached, the status indicator turns yellow to indicate that Maya has been paused. A red line highlights the next line of code to be executed.
  8. Now you can use all the debugging features of Wing IDE to examine the state of your program. Press F5 or the green play button to return control to Maya.
Note

For more details on how to use Wing IDE, see its Help documentation.

Registering a Python function as a MEL procedure

You can use the createMelWrapper function to register a Python function as a MEL procedure. Then, when the MEL procedure is invoked, it calls the Python function, passing any arguments it receives and returning the function's result.

For more information regarding this function, refer to the melutils.py file in:

C:\Program Files\Autodesk\Maya2013\Python\lib\site-packages\maya\mel

Or, execute the following in the Script Editor:

import maya.mel
help(maya.mel.createMelWrapper)

Advantages of using this function:

Example

The following is an example of how to use the createMelWrapper function to register a Python function as a MEL procedure in order to use the Rmb Command attribute in the asset (formerly container) node.

  1. Save the following as rmbScript.py in your Maya2013/scripts directory:
    import maya.cmds as cmds
    import maya.mel as mel
    def exCoNotes(node):
        if(cmds.nodeType(node)=='container'):
           objCont = node 
        else:
           objCont = cmds.container(q=True, findContainer=node)
        exec(cmds.getAttr(objCont+'.notes'))
        pyfunction = 'main("'+node+'","'+objCont+'")'
        exec(pyfunction) 
        cmds.select(node, r=True)
    def setThisContainerCurrent(node):
        if(cmds.nodeType(node)=='container'):
           objCont = node 
        else:
           objCont = cmds.container(q=True, findContainer=node)
        cmds.container(objCont, e=True, c=True)
        cmds.select(node, r=True)
    def rmbMyContainerScript():
        return ("Execute Container Notes", "exCoNotes",
        "Set This Container Current", "setThisContainerCurrent")
    
  2. Execute the following from a Python tab in the Script Editor. This will create the MEL wrapper scripts needed:
    from rmbScript import *
    import maya.cmds as cmds
    import maya.mel as mel
    mel.createMelWrapper(rmbMyContainerScript,retType='string[]')
    mel.createMelWrapper(exCoNotes)
    mel.createMelWrapper(setThisContainerCurrent)
    
  3. Create an object.
  4. Select Assets > Advanced Assets > Create and add rmbMyContainerScript to the asset’s Rmb Command attribute.
    Note

    You can also use this step with Assets with Transform; however, the node type would be dagContainer instead.

  5. Add something similar to the following to the asset node’s Notes section:
    def main(node, container):
        print node
        print container
    
  6. Enter in the MEL tab in the Script Editor:
    rehash;
    
  7. your object and select Custom from the marking menu. You can now see your custom right-mouse-button menu. See Assign a custom command or menu to an asset for more information.

Creating keyframes for an animated curve

You can use this Python example script to create an animated curve and set its key frames:

import maya.OpenMaya as om
import maya.OpenMayaAnim as oma
def addkeys(plugName, times, values, changeCache):
   # Get the plug to be animated.
   sel = om.MSelectionList()
   sel.add(plugName)
   plug = om.MPlug()
   sel.getPlug(0, plug)
   # Create the animCurve.
   animfn = oma.MFnAnimCurve()
   animCurve = animfn.create(plug, oma.MFnAnimCurve.kAnimCurveTL)
   
   # Copy the times into an MTimeArray and the values into an    MDoubleArray.
   timeArray = om.MTimeArray()
   valueArray = om.MDoubleArray()
   for i in range(len(times)):
      timeArray.append(om.MTime(times[i], om.MTime.uiUnit()))
      valueArray.append(values[i])
   # Add the keys to the animCurve.
   animfn.addKeys(
      timeArray,
      valueArray,
      oma.MFnAnimCurve.kTangentGlobal,
      oma.MFnAnimCurve.kTangentGlobal,
      False,
      changeCache
   )

The sample code above adds keyframe animation to a channel (in other words, a plug). If you do not need to be able to undo the change, you can call it as follows:

addkeys('pCube1.tx', [1.0, 3.0, 5.0], [0.6, 1.2, 2.4], None)

If, however, you want to be able to undo changes, then call it as follows:

changeCache = oma.MAnimCurveChange()
addkeys('pCube1.tx', [1.0, 3.0, 5.0], [0.6, 1.2, 2.4], changeCache)

and then do the following to undo the changes:

changeCache.undoIt()
Note

For simplicity, this function assumes the plug specified by plugName is not currently animated. It is left as an exercise for you to add the checks required to make that assumption unnecessary.

Common information messages

When using the dgInfo command to obtain information about the DG, you may see information messages such as DIRTY BLOCK, DIRTY PROP and so forth. For a list of definitions for these messages, see Common info messages.

Creating an user event with ScriptJob

You can do this in a Python script using API calls. The following demonstrates how you would register a new user event type called myEvent:

import maya.OpenMaya as om
om.MUserEventMessage.registerUserEvent('myEvent')

To have a function called myFunc execute whenever the event occurs, do as follows:

def myFunc(data):
    print('Got a myEvent event!')
callbackId = om.MUserEventMessage.addUserEventCallback('myEvent', myFunc)

To send a myEvent event, do as follows:

om.MUserEventMessage.postUserEvent('myEvent')

To remove the callback function when done, do as follows:

om.MUserEventMessage.removeCallback(callbackId)