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.
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.
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.
To start a debugging session of a Maya Python script with Wing IDE 3.1 in Windows, do as follows:
import wingdbstub
Windows may pop up another authorization dialog box asking for authorization. You should allow this.
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\Maya2012\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:
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.
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")
from rmbScript import * import maya.cmds as cmds import maya.mel as mel mel.createMelWrapper(rmbMyContainerScript,retType='string[]') mel.createMelWrapper(exCoNotes) mel.createMelWrapper(setThisContainerCurrent)
def main(node, container): print node print container
rehash;
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()
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)