Upload
shonda-allison
View
239
Download
0
Embed Size (px)
Citation preview
© 2007 Autodesk 1
Python in Maya1. Maya Python API Scripts write scripts that access functionality previously available only through the
C++ API
1. Maya Python API Scripted Plug-ins define new commands, nodes, etc using Python like C++ plug-ins written in Python
2. Maya Python Standalone Apps like MLibrary-based C++ apps, but written in Python
Non UI
3. Using UI with Python in Maya
© 2007 Autodesk 2
Maya Python API Plug-ins....
• Define a Python module containing initializePlugin(), uninitializePlugin() functions
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerCommand("foo", cmdCreator)
except:
sys.stderr.write( "Failed to unregister" )
raise
© 2007 Autodesk 3
Maya Python API Plug-ins....
• Load from Plug-in Manager or loadPlugin command
maya.cmds.loadPlugin( "C:/foo.py" )
© 2007 Autodesk 4
Maya Python API Plug-ins....
• Can be split into multiple files
• Import other modules Not on PYTHONPATH Additional modules must be in same directory
• Load mechanism pushes and pops sys path
© 2007 Autodesk 5
Python API: Deriving from Proxies
Similar setup to C++ plug-ins
class scriptedCommand(OpenMayaMPx.MPxCommand):
def __init__(self):
OpenMayaMPx.MPxCommand.__init__(self)
def doIt(self,argList):
print "Hello World!"
• Parameter: self same as this in C++
© 2007 Autodesk 6
Python API: Creators/Initializers
• Similar to C++ plug-ins
• Pass Python function to MFnPlugin methods (cmdCreator to registerCommand() )
• Ownership is important
© 2007 Autodesk 7
Python API: Creators/Initializers…
Ownership is important
• Use OpenMayaMPx.asMPxPtr() Xfers ownership of newly-created command or node objects to
Maya
def cmdCreator():
return OpenMayaMPx.asMPxPtr(scriptedCommand())
© 2007 Autodesk 8
Python API: Enums
• Enums not individual types as in C++
• Instead, access as integer class members
• Python help() is useful to look at class setup
help( maya.OpenMaya.MSyntax )[....]
| kNoArg = 1 | | kNone = 1 | | kSelectionItem = 10 |
© 2007 Autodesk 9
Python API: O/S Types
• <iostream> types are not in Python Required by some Maya API classes
• Example: MPxData::writeASCII( std:ostream& out )
• Use MStreamUtils in Python for this:
class blindDoubleData( OpenMayaMPx.MPxData ):
[...]
def writeASCII( self, out ):
OpenMaya.MStreamUtils.writeDouble( out, self.__val, False )
© 2007 Autodesk 10
Python API: Examples
• Most code translates readily from C++ to Python# DG modifier to change perspective camera translateX
import maya.OpenMaya as om
sel = om.MSelectionList()
om.MGlobal.getSelectionListByName( "persp“, sel )
dagPath = om.MDagPath()
sel.getDagPath( 0, dagPath )
dagPath.fullPathName()
# Result: |persp #
mod = om.MDGModifier()
mod.commandToExecute( "setAttr persp.tx 5" )
mod.doIt()
maya.cmds.getAttr( "persp.tx" )
# Result: 5 #
mod.undoIt()
# Result: 28 #
© 2007 Autodesk 11
Python API: Examples
• Commands with arguments must use the MSyntax and MArgParser classes within a scripted MpxCommand: devkit/plug-ins/scripted/helixCmd.py
© 2007 Autodesk 12
© 2007 Autodesk 13
Python API: Differences vs C++
• No MStatus class - use exceptions instead
try:
fnPlugin.registerCommand( “spLs", cmdCreator )
except:
sys.stderr.write( "Command registration failed")
raise
• Catch error if registerCommand() fails
© 2007 Autodesk 14
Python API: Differences vs C++
• Detect error but allow code to keep running
try:
fnPlugin.registerCommand( “spLs", cmdCreator )
except:
sys.stderr.write( "Command registration failed")
pass
• Keyword pass used instead of raise
© 2007 Autodesk 15
Python API: Differences vs C++
• MStatus-like values used in specific situation Return value of MPxNode::compute()
return maya.OpenMaya.kUnknownParameter
© 2007 Autodesk 16
Python API: Differences vs C++
• MString and MStringArray classes have been replaced by Python native strings and string lists
import maya.OpenMaya as omsel = om.MSelectionList();om.MGlobal.getSelectionListByName( "persp", sel )path = om.MDagPath()sel.getDagPath(0, path )myString = path.fullPathName()print "Path is %s" % myString# Path is |persp #
myStrings = []sel.getSelectionStrings( myStrings )print myStrings# [u'persp'] #
• Makes for simpler code
© 2007 Autodesk 17
Python API: Differences vs C++
• Maya doc: en_US/API/class_m_script_util.html
• Python does not have a concept of pointers
• Use MScriptUtils for working with pointers and references Reference treated as a pointer
• MScriptUtils creates objects that can be passed as pointer or reference parameters convenience methods for transferring values between these objects and
native Python datatypes
© 2007 Autodesk 18
Python API: MScriptUtils
(C++) int MImage::getSize( unsigned int& width, unsigned int& height )
(Python) img = OpenMaya.MImage()
img.create( 512, 256 )
util = OpenMaya.MScriptUtil(0)
util2 = OpenMaya.MScriptUtil(0)
wPtr = util.asUintPtr() # creates a ptr object
hPtr = util2.asUintPtr() # creates another ptr object
OpenMaya.MScriptUtil.setUint( wPtr, 0 )
OpenMaya.MScriptUtil.setUint( hPtr, 0 )
img.getSize( wPtr, hPtr )
width = OpenMaya.MScriptUtil.getUint( wPtr ) # 512
height = OpenMaya.MScriptUtil.getUint( hPtr ) # 256
© 2007 Autodesk 19
Python API: More MScriptUtils....• Another example: working with arrays
(C++) int MImage::setDepthMap( const MFloatArray& depth,
unsigned int width, unsigned int height )
float *MImage::depthMap( MStatus *returnStatus ) const
(Python) img = OpenMaya.MImage()
img.create( 512, 256 )
depthArray = []
for i in range(0,512*256):
depthArray.append( i )
su = OpenMaya.MScriptUtil()
su.createFromList( depthArray, 512*256 )
img.setDepthMap( su.asFloatPtr(), 512, 256 )
readBack = img.depthMap()
for i in range( 0, 512*256 ):
readDepth = OpenMaya.MScriptUtil.getFloatArrayItem(readBack, i )
if( readDepth != i ):
print( "Depth error" )
© 2007 Autodesk 20
Python API Scripts
• Discussed using maya.cmds in Script Editor and modules and maya.OpenMaya* in Python scripted plug-ins separately
• Same usage distinctions as C++/MEL
• With Python, the line between a "script" and a "plug-in" is blurred
• Can access maya.OpenMaya* API classes outside of a scripted plug-in
© 2007 Autodesk 21
Python API Scripts: Example
• Example that accesses the MFnMesh ray-mesh intersection functionality
• Casts a ray along a given linear NURBS curve and places a sphere at the point where the curve intersects a mesh
© 2007 Autodesk 22
Python API Scripts: Example#devkit/plug-ins/scripted/meshIntersect.py
import maya.cmds as mcimport maya.OpenMaya as om
def meshIntersect_doIt(*args):(mesh, curve) = getSelected()
# get ray start/end fnCurve = om.MFnNurbsCurve( curve )numCVs = fnCurve.numCVs()curveStart = mc.pointPosition( curve.fullPathName() + ".cv[0]", w=True );curveEnd = mc.pointPosition( curve.fullPathName() + (".cv[%d]" % numCVs), w=True );source = om.MFloatPoint(curveStart[0], curveStart[1], curveStart[2])dir = om.MFloatVector(curveEnd[0]-source.x, curveEnd[1]-source.y, curveEnd[2]-source.z )
# cast rayhitPoint = om.MFloatPoint()fnMesh = om.MFnMesh( mesh )hit = fnMesh.closestIntersection( source, dir, None, None, True, om.MSpace.kWorld, 1.0, True, None, hitPt, None, None, None, None, None );# implement resultif( hit ):
mm.eval( "polySphere -r 0.1; move %f %f %f" % (hitPt.x, hitPt.y, hitPt.z) )
© 2007 Autodesk 23
Python API Scripts: Caveats
• Careful about undo API functionality is not automatically undoable in the same way
that MEL commands are With Python API code operating outside of an MPxCommand-
derived class, there is no formal interface to allow you to implement your own undo behaviour
• Careful with scripted plug-ins Importing the .py file is not the same as loading it from the Plug-in
Manager Will not register new commands/nodes
© 2007 Autodesk 24
Maya Python Standalones
• Autodesk provide the maya.standalone module for Creating standalone Maya command-line apps
• Similar to MLibrary-based standalone apps created with C++ Python-based standalones are platform independent
• Run using: mayapy Python interpreter that ships with Maya
© 2007 Autodesk 25
Standalone Example
• Sample application defaultShading.py Opens a Maya file Assigns the default shader to all meshes Saves the scene
• Written as a standalone Maya Python application Uses the optparse Python module for command line parsing
• Could be done platform-independently with MEL and "mayabatch -cmd“ Putting together proper command string can be difficult
• Could also be done in C++ as an MLibrary-based app
© 2007 Autodesk 26
Standalone Example.....from optparse import OptionParser[...other imports...]def assignDefaultShader( srcFile, dstFile ):[...]
def main( argv = None ):parser = OptionParser()parser.add_option("-s", "--sourceFile", dest="srcFile", help="process SOURCEFILENAME")parser.add_option("-d", "--destFile", dest="dstFile", help="save to DESTFILENAME")
(options,args) = parser.parse_args()
maya.standalone.initialize( name='python' )assignDefaultShader( options.srcFile, options.dstFile )
if __name__ == "__main__":main(sys.argv[1:])
© 2007 Autodesk 27
Running the Standalone Example
C:> mayapy defaultShading.py --help
usage: defaultShading.py [options]
options:
-h, --help show this help message and exit
-s SOURCEFILENAME, --sourceFile=SOURCEFILENAME
process SOURCEFILENAME
-d DESTFILENAME, --destFile=DESTFILENAME
save to DESTFILENAME
C:> mayapy defaultShading.py -s "C:/foo.mb" -d "C:/bar.mb"
processed C:/foo.mb into C:/bar.mb
© 2007 Autodesk 28
Using External Python Interpreters
• Can use Maya Python package in any Python 2.4.3 interpreter
• Just need to configure a few variables:
Put Maya site-packages directory in sys.path Set MAYA_LOCATION Put Maya bin directory in PATH environment variable
• mayapy interpreter does all this automatically for you
© 2007 Autodesk 29
External Python Interpreters.....
• Can do all this setup on the fly - recode our previous standalone example # Running from a script fileif __name__ == "__main__":
if( not os.environ.has_key("MAYA_LOCATION") ):print( "External Python interpreter - initialize Maya paths" )mayaDir = "C:\\Program Files\\Autodesk\\Maya8.5"sys.path.append( mayaDir + "\\Python\\Lib\\site-packages" )os.environ["MAYA_LOCATION"] = mayaDiros.environ["PATH"] = os.environ["PATH"] + ";" + mayaDir + "\\bin"
else:print( "mayapy interpreter - paths already initialized" )
import maya.standalonemaya.standalone.initialize( name='python' )
import maya.cmds as mcimport maya.OpenMaya as omimport maya.mel as mm
main(sys.argv[1:])
© 2007 Autodesk 30
External Python Interpreters.....
C:> mayapy defaultShading2.py -s foo.mb -d bar.mb
mayapy interpreter - paths already initialized
Processed foo.mb into bar.mb
C:> python defaultShading2.py -s foo.mb -d bar.mb
External Python interpreter - initialize Maya paths
Processed foo.mb into bar.mb
• Why do this? So that you can write (standalone) Maya Python scripts in your chosen external Python IDE
© 2007 Autodesk 31
Example: OpenGL from Python
• Easy to create MPxLocator-based nodes with custom draw routines in Python
• Access OpenGL through GL function table provided by the API
© 2007 Autodesk 32
Example: OpenGL from Python.......import maya.OpenMayaRender as OpenMayaRenderglRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()glFT = glRenderer.glFunctionTable()
class pyLocatorNode( OpenMayaMPx.MPxLocatorNode ):[.....]def draw( self, view, path, style, status ):
view.beginGL()glFT.glDisable(OpenMayaRender.MGL_LIGHTING)glFT.glBegin(OpenMayaRender.MGL_LINES)glFT.glColor3f( 1.0, 0.0, 0.0 )glFT.glVertex3f( 0.0, 0.0, 0.0 )glFT.glVertex3f( 3.0, 0.0, 0.0 )
glFT.glColor3f( 0.0, 1.0, 0.0 )glFT.glVertex3f( 0.0, 0.0, 0.0 )glFT.glVertex3f( 0.0, 3.0, 0.0 )
glFT.glColor3f( 0.0, 0.0, 1.0 )glFT.glVertex3f( 0.0, 0.0, 0.0 )glFT.glVertex3f( 0.0, 0.0, 3.0 )glFT.glEnd()glFT.glEnable(OpenMayaRender.MGL_LIGHTING)view.endGL()
© 2007 Autodesk 33
Example: Multi-threading
• Python has a convenient module for creating and managing multiple threads
import threading
class MyThread( threading.Thread ):
def run( self ):
doSomeStuff()
MyThread().start()
© 2007 Autodesk 34
Example: Multithreading.....
• Maya API, Command Engine, and DG are not thread-safe
• maya.cmds Throws an exception when called from outside the main thread
• maya.OpenMaya* functionality Exhibit undesirable behaviour when called from outside the main
thread
• maya.utils.executeInMainThreadWithResult() Allows safe execution of a Python command from outside the main
thread
© 2007 Autodesk 35
Example: Multithreading......
• Using Python's threading and socket libraries: Create a simple Command Port replacement
• Spawn a thread that: opens a socket listens for Python commands runs them
© 2007 Autodesk 36
Example: fakeCommandPort# Location: devkit/scripts/fakeCommandPort.py
def openSocket(portNum):mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )mySocket.bind ( ( '', portNum ) )mySocket.listen ( 1 )
channel, details = mySocket.accept()msg = channel.recv(4096)res = maya.utils.executeInMainThreadWithResult( msg )resString = pickle.dumps(res) # serialize data structurechannel.send( resString ) channel.close()
class CmdPortThread( threading.Thread ):def run( self ):
openSocket(5722)
def fakeCommandPort():CmdPortThread().start()
© 2007 Autodesk 37
Example: Multithreading......
• From Maya Run fakeCommandPort() to start the server
• Uses the "pickle" library to serialize Python data structures for transmission over the network and reconstruct on the client side
• From an external Python interpreter, do:s=socket.socket( socket.AF_INET, socket.SOCK_STREAM )
s.connect( ('localhost',5722) )
s.send( “maya.cmds.ls()” )
resultBuf = s.recv(4096)
result = pickle.loads(resultBuf) # restore serialized structure
# [u'time1', ....] #
© 2007 Autodesk 38
© 2007 Autodesk 39
Using UI with Python in Maya
• Using loadUI
• Using PyQt•Install PyQt:
•../maya2011-x64/devkit/other/PyQtScripts
© 2007 Autodesk 40
Example: Using the loadUI command
# devkit/scripts/edytor_loadUI.py
# devkit/scripts/edytor.ui
dialog = cmds.loadUI(uiFile='/q/devkit/scripts/edytor.ui')
cmds.showWindow(dialog)
© 2007 Autodesk 41
Example: Using PyQt (1)# devkit/scripts/windowPyQt.py
import sip
import maya.OpenMayaUI as mui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def getMayaWindow():
ptr = mui.MQtUtil.mainWindow()
return sip.wrapinstance(long(ptr), QObject)
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.setObjectName('mainUI')
self.mainLayout = QVBoxLayout(self)
self.myButton = QPushButton('myButton')
self.mainLayout.addWidget(self.myButton)
global app
global form
app = qApp
form = Form(getMayaWindow())
form.show()
© 2007 Autodesk 42
Example: Using PyQt (2)# devkit/scripts/windowPyQt2.py
# devkit/scripts/edytor.ui
# devkit/scripts/edytor.py
class StartQt4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_notepad()
self.ui.setupUi(self)
# here we connect signals with our slots
QtCore.QObject.connect(self.ui.button_open,QtCore.SIGNAL("clicked()"), self.file_dialog)
def file_dialog(self):
fd = QtGui.QFileDialog(self)
self.filename = fd.getOpenFileName()
...
def main():
global app
global form
app = QtGui.qApp
form = StartQt4(getMayaWindow())
form.show()
© 2007 Autodesk 43
Maya Python API Guide1. Selecting with the API2. Command plug-ins3. DAG Hierarchy4. Writing a Shading Node5. Dependency graph plug-ins6. Manipulators7. Shapes8. Writing a Hardware Shading Node9. Writing a Custom Transform Node10. Writing a Deformer Node11. Writing File Translators12. Multithreading plug-ins13. Polygon API
© 2007 Autodesk 44
Selecting with the API1. MselectionList
•devkit/plug-ins/scripted/getActiveSelection2.py•devkit/plug-ins/scripted/getActiveSelection.py
2. MitSelectionList•devkit/plug-ins/scripted/getActiveCVs.py
3. selectByName()•devkit/plug-ins/scripted/selectByName.py
© 2007 Autodesk 45
Command plug-ins 1
1. Command with undo and redo •#Overloading “redoIt” and “undoIt” methods inherited from MPxCommand•devkit/plug-ins/scripted/zoomCameraCmd.py
2. Returning results to MEL•#Overloading “setResult” and “appendToResult” methods inherited from MPxCommand•devkit/plug-ins/scripted/basicObjectSet.py
• Command with argments•#MSyntax—Used to specify flags and arguments passed to commands•#MArgDatabase—Class used to parse and store all flags, arguments, and objects passed to a command•devkit/plug-ins/scripted/helixCmd.py
© 2007 Autodesk 46
Command plug-ins 2
1. Contexts•#Contexts in Maya are modes which define how mouse interaction will be interpreted•devkit/plug-ins/scripted/moveTool.py
2. Tool property sheets•#Tool property sheets are interactive editors for displaying and modifying the properties of a context
3. MPxToolCommand•#The MPxToolCommand is the base class for creating commands that can be executed from within a context•devkit/plug-ins/scripted/moveTool.py
4. Attaching a plug-in to a Maya menu•#MFnPlugin.addMenuItem()