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:
- By registering your Python function as a MEL procedure, you do not need to source the MEL script if the procedure and the script share the same name. By comparison, if you are using Python, you must import the script explicitly before you can access the functions or classes it contains. Using this wrapper function allows Maya to source on the fly and not have to have every script available loaded at all times.
- Some attributes in Maya only accept MEL procedures. For example, in the asset (formerly container) node, the Rmb Command attribute under the Context Properties section only accepts MEL procedures.
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.
- 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")
- 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)
- Create an object.
- 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.
- Add something similar to the following to the
asset node’s
Notes section:
def main(node, container): print node print container
- Enter in the MEL tab in the
Script Editor:
rehash;
- 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()
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)