Class 10: Python wrap-up

Today's menu

Topics:

Image manipulation

Start by downloading this image module (from http://pages.central.edu/emp/johnsonm/conciseintro/), and placing it in your 'scripts' dir.

Now try this:


import image, random

dir(image)

help(image)



i = image.ImagePPM.new((256,256))

for x in range(256):

  for y in range(256):

    i.putpixel((y,x),(100,160,200))

i.save("C:/Users//Desktop/fun.ppm")



# now look in your desktop :)

How can we generate patterns?



Python in Maya - intro, history

At first there was/is Maya Python (maya.cmds), from Autodesk. MEL calls were wrapped in Python, that's about it.

At Luma Pictures, developers began 'PyMEL' (pymel), and started a Google forum to spread the word (and did!!). pymel wraps around maya.cmds :)

Today we have PyMEL officially being bundled by Autodesk.. So there are two ways to "do Python in Maya".

Be sure to do the PyMEL samples below, in Maya 2011 or later.

What's in maya.cmds, pymel


import maya.cmds 

import maya.mel

dir(maya.cmds)

dir(maya.mel)





from pymel.core import *

dir()

maya.cmds is strings-based, pymel is object-based


for n in maya.cmds.ls():

  print type(n)

# oh no!!! maya.cmds is simply MEL in Python's clothing :(



for n in ls():

  print type(n)

# THIS is why PyMEL is WAAAAAAAAAAAAAAAAAAY BETTER!!!!!!!!!

Trying out maya.cmds

The idea is pretty simple - commands you need to run are all 'maya.cmds.something()', with extra inputs being specified inside the '()' (or 'cmds.something()' if you use the import shortcut below).




import maya.cmds as cmds

cmds.plane()



cmds.sphere(radius=4)

cmds.move(2.0, 1.0, 1.0, 'nurbsSphere1')



cmds.sphere(n='ball')



cmds.ls(type='nurbsSurface')



cmds.ls(selection=True)



cmds.getAttr('ball.translate')

cmds.getAttr('ball.translateX')

cmds.setAttr('ball.translateX', -5.0)



# something more fancy

import math

for i in range(360):

 x = math.cos(i*3.1415/180)

 y = math.sin(i*3.1415/180)

 cmds.sphere(r=0.01,p=[x,y,0])

UI creation

MEL remains a powerful, simple way (although verbose) to create UI in Maya. So we can use maya.cmds (or pymel) to create them in Python (since they call MEL anyway). FYI - a completely different, and new, way is to code your UI in PyQt (or 'QtPy') - Qt is a very popular toolkit (originally from Trolltech in Norway, since acquired by Nokia) that is used in cellphones/PDAs as well as desktops.

Here are some UI examples.


cmds.promptDialog(button=['OK'])

userText = cmds.promptDialog(query=True, text=True)

print "You entered " + userText + " in the dialog box :)"



cmds.promptDialog(

        title='Create object',

        message='Enter Object Name:',

        button=['Sphere', 'Cancel'],       

        defaultButton='Sphere',

        cancelButton='Cancel')

 

text = cmds.promptDialog(query=True, text=True)

cmds.polySphere(n=text)



cmds.promptDialog(

        title='Sphere creation',

        message='Enter sphere\'s name:',

        button=['Do it', 'Cancel'],       

        defaultButton='Create sphere',

        cancelButton='Cancel')

 

sphNm = cmds.promptDialog(query=True, text=True)

cmds.polySphere(n=sphNm)

As you can see, the commands are not bad (not complex). Here is another piece of code that shows how to create radio-buttons. This is a screenshot.


win =  cmds.window()

cmds.columnLayout()

 

cmds.radioCollection('Shapes')

cmds.radioButton('Sphere', label = 'Sphere')

cmds.radioButton('Cube', label = 'Cube')

cmds.radioButton('Cylinder', label = 'Cylinder')

cmds.radioButton('Torus', label = 'Torus')

 

cmds.button( label='OK', command="print cmds.radioCollection('Shapes', query=True, select=True)")

 

cmds.showWindow(win)

Note that you specify the command for the button, in a separate 'callback' function (that Maya will call back, (only) when the user clicks the button. Here's what the callback will look like:




# note - 'self' MUST always be the first argument in a callback func definition

def createUsersObj(self):

    us = cmds.radioCollection('Shapes', query=True, select=True)

    print us

    if us=='Sphere':

     cmds.sphere()

    elif us=='Cylinder':

     cmds.cylinder()

    elif us=='Torus':

     cmds.torus()

    else:

     cmds.cube()







win =  cmds.window()

cmds.columnLayout()

 

cmds.radioCollection('Shapes')

cmds.radioButton('Sphere', label = 'Sphere')

cmds.radioButton('Cube', label = 'Cube')

cmds.radioButton('Cylinder', label = 'Cylinder')

cmds.radioButton('Torus', label = 'Torus')

 

cmds.button( label='OK', command=createUsersObj)

 

cmds.showWindow(win)

Another UI example:


# select a bunch of nodes, then run the following



import maya.cmds as mc



def createMyLayout( obj, parent ):

    mc.setParent(parent)

    c = mc.columnLayout()

    text = mc.textField(text=obj)

    button1 = mc.button(label="one")

    button2 = mc.button(label="two")

    return c



myLayouts = []

w = mc.window("example", title="Sample UI creation")

c = mc.columnLayout()

for selectedObject in mc.ls(sl=True):

    myLayouts.append(createMyLayout(selectedObject, c))

mc.showWindow(w)

Here is how to set keyframes.. Start a new scene, set fps to 50, and set the frame range from 1 to 300 (we'll need 252 frames). Then run this:




import maya.cmds as cmds

cmds.polySphere(name = "bouncing", radius = 2)

g = -0.04

v0y = 0.8

v0x = 0.1

pos0y = 2

pos0x = 0

for itr in xrange(0,6):

 for tx in xrange(0,42):

  posy = pos0y + v0y*(tx-1) + g*(tx-1)*(tx-1)/2

  posx = pos0x + v0x*((itr*42) + tx-1)

  cmds.setKeyframe("bouncing", attribute="translateY", value=posy, t=(itr*42) + tx )

  cmds.setKeyframe("bouncing", attribute="translateX", value=posx, t=(itr*42) + tx )

In the above, we're calling maya.cmds.polySphere() to create a ball, and maya.cmds.setKeyframe() to set a keyframe (given a node, attribute, value, time). So straightforward!

Next, let's open a 'P3' .ppm text file (that contains image pixels), and "plot" it in Maya.. Here is the image file (as ASCII!).


import maya.cmds

# before proceeding, EDIT sgn.ppm, remove these four 'header' lines:

# P3

# # CREATOR: The GIMP's PNM Filter Version 1.0

# 99 99

# 255

f = open("c:/test/sgn.ppm")

lines = f.readlines()

lines

rx, ry = 99,99    

for i in range(ry):

    for j in range(rx):

        pxl = []

        for k in range(3):

            indx = k + j*3 + 3*i*rx # ry-1-i

            pxl.append(int(lines[indx].rstrip('\n')))

        rd = (pxl[0]+pxl[1]+pxl[2])/(3*255.0)

        maya.cmds.sphere(r=rd)

        maya.cmds.move(j,i,0)

f.close()





# variation

import maya.cmds

f = open("c:/temp/sgn.ppm")

lines = f.readlines()

rx, ry = 99,99    

for i in range(ry):

    for j in range(rx):

        pxl = []

        for k in range(3):

            indx = k + j*3 + 3*(ry-1-i)*rx # ry-1-i

            pxl.append(int(lines[indx]))

        rd = 5*(pxl[0]+pxl[1]+pxl[2])/(3*255.0)

        maya.cmds.polyCube(w=1,h=1,d=rd)

        maya.cmds.move(j,i,0*rd/2)

f.close()

Here's a 'chain' script:


import maya.cmds as mc



mc.polyCylinder(ax=[1,0,0])

mc.scale(4,1,1)

mc.sphere(p=[6,0,0])

mc.duplicate()

mc.move(3,0,0)

for i in range(1,11): 

    mc.duplicate(st=True)

    

mc.select(["pCylinder1","nurbsSphere1"],r=True)

mc.constrain(hinge=True, o=(0, 2.5, 0), i=0 )

mc.select("pCylinder1")

mc.setAttr("rigidBody1.active", 0)



for i in range(1,12):

    mc.select("nurbsSphere"+str(i))

    mc.select("nurbsSphere"+str(i+1),tgl=True)

    mc.constrain(hinge=True, o=(0, 2.5, 0), i=0 )



mc.move(1,0,0,"rigidHingeConstraint1",r=True)

mc.gravity(pos=(0,0,0), m=9.8, dx=0, dy=-1, dz=0)

mc.select("nurbsSphere*")

sph = mc.ls(selection=True)

mc.connectDynamic(sph,f="gravityField1")



# set pb range to 200 frames, then play :)

Exercise - how to create 'symmography' cobweb curves??



Things are way simpler in pymel, compared to maya.cmds

pymel was explicitly conceived (by Chad Dombrova and others from Luma Pictures - THANK YOU!) to be object-oriented, as opposed to maya.cmd's "stringy" feel (because it is simply a Python layer/wrapper for good old MEL calls).

Calls use standard Py objects such as lists/tuples.


import pymel.core

pymel.core.curve(d=1,p=[(0,0,0),(1,1,1)])

Because pymel is object-oriented, it 'comes' with useful functionality pre-built. In the example below, you can see that its 'mel' object contains a MEL-language call "natively".


values = ['one', 'three', 'two', 'three', 'four']

# call a MEL function, via 'eval' 

maya.mel.eval( 'stringArrayRemoveDuplicates( {"'+'","'

.join(values)+'"})')



values = ['one', 'three', 'two', 'three', 'four']

# much simpler - pymel's 'mel' includes stringArrayRemoveDuplicates()

mel.stringArrayRemoveDuplicates(values) 

Running MEL procs from within pymel

There are times when you have a well-debugged,efficient piece of MEL code you like, and want to 'somehow' run that from within Python. How?


# 1. define

mel.eval( '''global proc myScript( string $stringArg, 

float $floatArray[] )

{ float $donuts = `ls -type camera`;}''')



# 2. use 

mel.myScript('test',[0.1,0.7])

mel.myScript('test',[]) # MelConversionError is thrown

pymel helps you 'string together' calls


// in MEL:

sphere();

string $sel[] = `ls -sl`;

string $shapes[] = `listRelatives -s $sel[0]`;

string $conn[] = `listConnections -s 1 -d 0 $shapes[0]`;

setAttr ($conn[0] + ".radius") 3;



# Back in Py:

import pymel.core as pm

pm.sphere()

pm.selected()[0].getShape().inputs()[0].radius.set(12) # wow

pymel's nodes - RICH functionality


camTrans, camShape = camera()  # create a new camera



camShape.setFocalLength(100)

fov = camShape.getHorizontalFieldOfView()

print fov



camShape.dolly( -3 )

camShape.track(left=10)



dir(cam) # all of cam's methods/members

Another pymel example

Again, based on a node's type, we can call appropriate methods.


s = polySphere()[0] # just shape



dir(s)



if s.visibility.isKeyable() and not s.visibility.isLocked():

    s.visibility.set(False)

    s.visibility.lock()

    print s.visibility.type()



dir(s.getChildren()) # what are we seeing?? 





dir(s.getChildren()[0])

pymel - reach deep into nodes

You can also do component-level processing, using simple-looking calls:


s = polySphere()[0] # just shape



# s.faces is a list of all faces 

for face in s.faces:

    if face.getNormal('world').y > 0.0:

       select(face, add=1)

pymel - creating lines

Here is how to draw straight lines (curves of degree 1):


import pymel.core as pm, random

for i in range(100):



 p1 = (random.randint(-45,45),random.randint(-45,45),random.randint(-45,45))

 p2 = (random.randint(-45,45),random.randint(-45,45),random.randint(-45,45))



 pm.curve(p=[p1,p2], d=1)

Comparison: three eval()s

There is MEL's eval() command that is available via maya.cmds.eval() and also pymel's mel.eval(), as shown below. In addition, entirely separately, Python's native eval() call is also there(to evaluate a **Python** string).


# maya.cmds

maya.mel.eval("curve -d 3 -p 0 0 0 -p 1 0 0 -p 0 1 0 -p 0 0 1")



# pymel

mel.eval("curve -d 3 -p 0 0 0 -p 1 0 0 -p 0 1 0 -p 0 0 1")



# Python's built-in eval()

eval('camera()')

Manipulating the Maya 'optionVar' database - so simple

Maya stores ALL CUSTOMIZATIONS in a database (maintained in a MEL script).


if 'numbers' not in optionVar:

    optionVar['numbers'] = [1,24,47]

optionVar['numbers'].append(9)

numArray = optionVar.pop('numbers')

Comparable MEL commands are shown here.

Getting/setting globals

All globals are in a pymel dictionary called 'melGlobals':


melGlobals['$gMainFileMenu']

melGlobals['$gGridDisplayGridLinesDefault'] = 15

Some custom operators

pymel comes with operators for parenting nodes, and for connecting and disconnecting attrs.


# create a camera and a sphere

camXform, camShape = camera()

sphere = polySphere()[0]



sphere | camXform  # parent the camera to the sphere



camXform.tx >> camXform.ty  # connect operator



camXform.tx // camXform.ty  # disconnect operator

UI

Creating pymel-based UI is easy/fun. Try this:


from pymel.core import *

win = window(title="My Window")

layout = columnLayout()

chkBox = checkBox(label = "My Checkbox", value=True, parent=layout)

btn = button(label="My Button", parent=layout)

def buttonPressed(*args):

    if chkBox.getValue():

        print "Check box is CHECKED!"

        btn.setLabel("Uncheck")

    else:

        print "Check box is UNCHECKED!"

        btn.setLabel("Check")

btn.setCommand(buttonPressed)

win.show()

Here are many more examples.

'Porting' MEL to pymel (and vice-versa!)

When you know one language, learning additional ones becomes easier - this is because you can compare the new language(s) to your first one..

Here is a MEL to Python 'transition' guide (very barebones!)..



"Script plugins"

Interestingly, you can write 'Python script plugins', which make use of Python-wrapped C++ Maya API calls. In other words - in the past, Maya plugins *had* to be coded in C++, but now, we can use Python. Advantage - cleaner/simpler syntax, and the ability to mix in code from dozens/hundreds of other Python modules.

Here is an example: download this file (helixCmd.py) to any directory (eg. /tmp or your home dir), and load it into Maya via the plugin manager (as you would, any other '.so' plugin). Then run this in the Python script-ed win:


import maya

maya.cmds.spHelix(p=0.3, r=7)

Cool! The helix curve that shows up in the scene, via the maya.cmds.spHelix() Python call that you just made, was generated from Python code contained in helixCmd.py. For fun, you can also run the plugin code via MEL (be sure to switch to the MEL tab first!):


// we're running, via MEL, Python code in 

// our 'helixCmd.py' plugin file :)

spHelix -p 0.4 -r 10; 

Study the code in helixCmd.py, make minor modifications (eg. look for the place where CV x,y,z values are computed, and set them to random (hint: 'import random') values :) Re-load the plugin, and run again (MEL or Python) - you should be able to see the result of your changes.

Making changes to existing code and observing what that does is one good way to learn programming. Another is to have a project/pet idea that you want to try out, and use that as a means to learn (ie look up necessary calls to make the code do what you want).

After more reading up and experimentation, you should be able to write Python script plugins in these categories:

So how do you learn more? Look at C++ plugin code - you will be able to 'port' these to Python without much effort. A huge collection of C++ plugin source files can be found in the '....devkit/plug-ins' directory of Maya's installation.

Likewise, a growing collection of Python "scripted" plugins are being shipped as well - look in the ..../devkit/plug-ins/scripted subdirectory. Here is documentation o nhow to use the ones that are included with Maya 2012. And here are several .py script plugin sources, mixed in with C++ (".cpp" and ".h") files..

Maya Python resources



What was left out?

Out of necessity (not enough time, too early, etc.), these had to be left out:

If you need your Python foundation to be complete, you do need to invest some effort in learning the above on your own. Given what you know, this shouldn't be hard at all.


FINIS

That's all, for now. Hope you UTILIZE, ENJOY AND PROFIT from "coding" throughout your career.. Good luck, be in touch!