Maya Python for Games and Film

Adam Mechtley, Ryan Trowbridge

Mentioned 2

Maya Python for Games and Film is the first book to focus exclusively on how to implement Python with Maya. Written by trusted authorities in the field, this in-depth guide will help you master Maya Python, whether you're a seasoned technical artist looking to make the transition from MEL to Python or an aspiring artist not wanting to scramble for information.

More on Amazon.com

Mentioned in questions and answers.

I am trying to create a callback in my Maya plugin that runs every time a new object is created. The callback works but the data object passed is of NoneType. I want to access the newly created object in my callback function, how do i do that?

g_Callbacks = list()

def initializePlugin( obj ):
    g_Callbacks.append( OpenMaya.MEventMessage.addEventCallback( "DagObjectCreated", callback ) )

    ...

def callback( data ):
    print data.apiTypeStr()

From the comments it sounds like there are two separate issues.

Notificiations on object creation are 'free', in the sense that you can do this without the API by using a scriptJob. Here's a trivial example

def new_object_callback():
    print "created", cmds.ls(sl=True)

cmds.scriptJob(e=('DagObjectCreated', new_object_callback))

The callback here is just a plain python script , but it could be a function created by an MPXCommand (that link is a good intro to a very simple command plugin, btw).

There is one limitation here: the creation callback will fire once per undo block. A menu item, button or script creates a single undo --- which means that an action which creates multiple objects will only get that notification once (and the example code above will only print out the message for the last created object).

Another way of reading the question is how to fire a callback when you create the object yourself in a plugin. That's a simpler problem, although plugins for creating objects are kind of wordy to write. (A decent intro here -- the python stuff is interleaved with C++, because the docs for all of this are still written for C++) Every plugin node class has to have a function called initialize, which will be called every time the plugin creates a new node, so you can use that to call any code you need to run at startup.

OpenMaya is a real pain in the butt, since you're basically writing C++ through Python. Here's a couple of decent references:

http://www.amazon.com/Maya-Python-Games-Film-Reference/dp/0123785782 http://www.amazon.com/Practical-Programming-Python-Robert-Galanakis/dp/1849694729

this is my code:

def animate(*arg):
    cvList = []
    myList = cm.ls(sl =True)
    randList1 = [rand.uniform(-10,10)for items in range(10)]
    for i in myList:
        cvList = cm.ls(i+".cv[:]" , flatten = True)
    for j in cvList:
        cm.setKeyframe(j , ".cv[0].xValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].xValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].yValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].yValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].zValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].zValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].xValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].xValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].yValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].yValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].zValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].zValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )


animWindow = cm.window(t = 'Animation controls' , w = 150 , menuBar = True , bgc = [.2,.2,.2])
cm.menu(l = 'About' , tearOff = False )
cm.menuItem( l = 'Version 1.0.0')
cm.columnLayout()
cm.text(l = 'Time 1' , w = 150)
time1 = cm.intField()
cm.text(l = 'Time 2' , w = 150)
time = cm.intField()
cm.button(l = 'Set key' , c = animate , w = 150, bgc = [.3,.3,.3])
cm.showWindow(animWindow)

this is the error i got:

# NameError: global name 'time1' is not defined #

i know after that maya wanna tell me time2 is the same

i used this in this one and it's worked:

def tinyRandomize(*arg):
    myList = cm.ls (sl = True)
    randoms = [rand.uniform(cm.floatField(Ceil, q = True , v = True),0.5)for i in range(30)]
    for objects in myList:
        cm.xform('%s.cv[0]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[1]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[2]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[3]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[4]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[5]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[6]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[7]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[8]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[9]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[10]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[11]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[12]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[13]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[14]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[15]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[16]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[17]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[18]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[19]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])
        cm.xform('%s.cv[20]'%objects , r = True , t = [(rand.choice(randoms)),(rand.choice(randoms)),(rand.choice(randoms))])

i added a gui for this def and it's completely worked

please help me.

best regards

you declared the variable time1 outside the def animate. When the function tries to run it doesn't know what time1 is.

@Argiri's answer is the right way to go in the long term - putting UI and callback functions into a class is a cleaner, better way to organize the code.

To make it really clear what's causing the problem, try this:

from functools import partial
import random as rand

def animate(time1, time2, ignore):
    print time1, time2, ignore
    cvList = []
    myList = cm.ls(sl =True)
    randList1 = [rand.uniform(-10,10)for items in range(10)]
    for i in myList:
        cvList = cm.ls(i+".cv[:]" , flatten = True)
    for j in cvList:
        cm.setKeyframe(j , ".cv[0].xValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].xValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].yValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].yValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].zValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[0].zValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].xValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].xValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].yValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].yValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].zValue" , t = cm.intField(time1 , query = True , value = True) , v = rand.choice(randList1) )
        cm.setKeyframe(j , ".cv[1].zValue" , t = cm.intField(time2 , query = True , value = True) , v = rand.choice(randList1) )


animWindow = cm.window(t = 'Animation controls' , w = 150 , menuBar = True , bgc = [.2,.2,.2])
cm.menu(l = 'About' , tearOff = False )
cm.menuItem( l = 'Version 1.0.0')
cm.columnLayout()
cm.text(l = 'Time 1' , w = 150)
time1 = cm.intField()
cm.text(l = 'Time 2' , w = 150)
time2 = cm.intField()
cm.button(l = 'Set key' , c = partial(animate, time1, time2) , w = 150, bgc = [.3,.3,.3])
cm.showWindow(animWindow)

the module functools includes an object called partial, which bundles an set of arguments with a function so they can be called together. By setting the button command to a partial at the time the code runs, you're making sure that when the callback is run the intField objects are going to the function -- so when the def asks the two fields what they are it knows how to find them.

All that said, use a class for things like this. If you're not clear on classes take a few days off from doing maya stuff to learn them - it's really important for the quality of your future code.

You should probably check out Mechtley's book on Maya python (especially the chapter on GUI code). There are also useful discussions here, here, and here

PS: Somebody will probably post a fix that puts 'global' in front of time1 and time2 and 'global time1, time2' inside of the animate def. Don't do it that way. Globals = evil.

Realated tags

globalmayapython