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

You can use any debugger to debug your Python scripts. Several IDEs, such as Eclipse and Wing IDE, come with their own debuggers. These have convenient graphical interfaces for debugging your scripts. See the documentation for your preferred IDE for information on how to start a debugging session.

Python also comes with its own built-in debugger, pdb. pdb has a text interface and can be invoked directly from within a script in the Script Editor. You will need to import the pdb module, then set a breakpoint using pdb.set_trace():

import pdb
pdb.set_trace()

When execution reaches pdb.set_trace(), the input dialog will appear. You can use standard commands to continue, step-into, print values, or do other debugging operations. An input dialog box will appear whenever pdb needs input.

For more information on how to use pdb see The Python Debugger.

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 the ..\Python\lib\site-packages\maya\mel folder of your Maya installation directory.

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 the scripts directory of your preferences folder:
    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. Right-click 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.

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)