Rewrite this as a Python Class?

116 views
Skip to first unread message

PixelMuncher

unread,
Jul 26, 2011, 6:01:13 PM7/26/11
to python_inside_maya
Hi all:
Perhaps this is too much to ask, but I've been studying up on Python
classes and trying to figure out how to use them in my Maya Coding. I
haven't found any good Maya examples.
If someone has a few minutes , maybe you could show how this eye
rigging code could be written as a class?
Thanks much.

def eye_rig(eyeObj,eyeTarget,controllers):
"Create EyeRig weighted between target (via aimContraint) and
animation override"

# Create control hierarchy of groups or locators:
#controllers = ['anim','aim','out']
controlsList = []
testing = 0

# For each controller, create a locator and add it to the list
of controllers
for control in controllers:
objTmp = eyeObj+'_'+control
cmds.spaceLocator(n=objTmp)
controlsList.append(objTmp)

#Final control (last item in list) will receive all the
rotations and pass them to the eye via parentConstraint.
# It will have UD attrs to weight inputs of other controls
finalControl = controlsList[-1]

# For each controller, we'll create a multDiv. We pipe the
control's rotation into the md, weight it w/UD attr on 'out' object.
# Rotations coming out of multDivs are added w/pma node and
passed to 'out' object

# Create pma node first, so md can be connected when created:
sumNodeTmp = cmds.createNode('plusMinusAverage',n=eyeObj
+'eyeRo t_sum')
print 'sumNodeTmp:',sumNodeTmp

# Summed rotations piped into 'out.rotate' (This is the last
item in the 'controlsList' List):
cmds.connectAttr(sumNodeTmp+'.output3D',finalContr ol
+'.rotate',force=1)

# Create _md node for each controller rotations, pipe into
rotation sum
count = 0
while count < (len(controlsList)-1):
nodeTmp = controlsList[count]
attrTmp = controllers[count]
print 'nodeTmp:',nodeTmp
# For each controller, create a MultDiv to weight the
rotation.
# They will be weighted by a custom attr on the 'out'
object:
mdTmp = cmds.createNode('multiplyDivide',n=nodeTmp+'_md')
# Connect rotations to multDiv.input1 (more rotation
influences can be added)
cmds.connectAttr(nodeTmp+'.rotate',mdTmp
+'.input1' ,force=1)
# Pipe the md output to the rotation sum:
connectAttr(mdTmp+'.output',sumNodeTmp
+'.input3D['+str(count)+']',force=1)

# Create UD attrs on 'out' (last item in list)to weight the
rotations of each controller.
# Pipe UD into the corresponding md nodes.

# Create UD Attrs on 'out'
cmds.addAttr(finalControl,ln=attrTmp,min=0,max=1,d
v=1,at='float')
cmds.setAttr (finalControl+'.'+attrTmp, e=1, keyable=1)# If
not keyable, won't show in channel box
# Connect Anim, Aim to inputs of respective multDiv:
connectAttrsTmp =['2X','2Y','2Z']
for tmpConnectAttr in connectAttrsTmp:
cmds.connectAttr(finalControl+'.'+attrTmp,mdTmp+'.
input'+tmpConnectAttr)

# If test, offset the controllers for visual testing:
if testing:
cmds.setAttr(nodeTmp+'.translateX',count+0.5)
count += 1

# Group the controllers
eyeRigGroupTmp = cmds.group(controlsList, n = eyeObj
+'_eyeDir_grp')

# Position the eyeRig at the eye
parentUnparent (eyeObj,eyeRigGroupTmp)

# Setup aimConstraint for eye
cmds.aimConstraint (eyeTarget,eyeObj+'_aim',offset = [0, 0, 0],
w = 1, aimVector = [1,0,0], upVector = [0,1,0], worldUpType =
'vector', worldUpVector = [0,1,0])

# Constrain Eye to "out" control
cmds.parentConstraint (eyeObj,finalControl, offset = (0, 0,
0 ), w = 1)

def parentUnparent(parent,children):
# Use temporary point-, scale- constraints to snap parent to
future child
delete( cmds.pointConstraint (parent, children, offset = (0, 0,
0 ), w = 1) )
delete( cmds.orientConstraint (parent, children, offset = (0,
0, 0 ), w = 1) )
delete( cmds.scaleConstraint (parent, children, offset = (1, 1,
1 ), w = 1) )

eye_rig('testX_eyeL','testX_eyeL_tgt',['anim','aim','out'])

张宇

unread,
Jul 27, 2011, 3:39:41 AM7/27/11
to python_in...@googlegroups.com
class fn:
    def eye_rig(self, eyeObj,eyeTarget,controllers):
        "Create EyeRig weighted between target (via aimContraint) and animation override"
    
        # Create control hierarchy of groups or locators:
        #controllers = ['anim','aim','out']
        controlsList = []
        testing = 0
    
        # For each controller, create a locator and add it to the list of controllers
        for control in controllers:
            objTmp = eyeObj+'_'+control
            cmds.spaceLocator(n=objTmp)
            controlsList.append(objTmp)
    
        #Final control (last item in list) will receive all the rotations and pass them to the eye via parentConstraint.
        #  It will have UD attrs to weight inputs of other controls
        finalControl = controlsList[-1]
    
        # For each controller, we'll create a multDiv.  We pipe the control's rotation into the md, weight it w/UD attr on 'out' object.
        #  Rotations coming out of multDivs are added w/pma node and passed to 'out' object
    
        # Create pma node first, so md can be connected when created:
        sumNodeTmp = cmds.createNode('plusMinusAverage',n=eyeObj+'eyeRo  t_sum')
        print 'sumNodeTmp:',sumNodeTmp
    
        # Summed rotations piped into 'out.rotate' (This is the last item in the 'controlsList' List):
        cmds.connectAttr(sumNodeTmp+'.output3D',finalControl +'.rotate',force=1)
    
        # Create _md node for each controller rotations, pipe intorotation sum
        count = 0
        while count < (len(controlsList)-1):
            nodeTmp = controlsList[count]
            attrTmp = controllers[count]
            print 'nodeTmp:',nodeTmp
            # For each controller, create a MultDiv to weight the rotation.
            #  They will be weighted by a custom attr on the 'out' object:
            mdTmp = cmds.createNode('multiplyDivide',n=nodeTmp+'_md')
            # Connect rotations to multDiv.input1 (more rotationinfluences can be added)
            cmds.connectAttr(nodeTmp+'.rotate',mdTmp+'.input1'  ,force=1)
            # Pipe the md output to the rotation sum:
            connectAttr(mdTmp+'.output',sumNodeTmp+'.input3D['+str(count)+']',force=1)
    
            # Create UD attrs on 'out' (last item in list)to weight therotations of each controller.
            #   Pipe UD into the corresponding md nodes.
    
            # Create UD Attrs on 'out'
            cmds.addAttr(finalControl,ln=attrTmp,min=0,max=1,dv=1,at='float')
            cmds.setAttr (finalControl+'.'+attrTmp, e=1, keyable=1)# Ifnot keyable, won't show in channel box
            # Connect Anim, Aim to inputs of respective multDiv:
            connectAttrsTmp =['2X','2Y','2Z']
            for tmpConnectAttr in connectAttrsTmp:
                cmds.connectAttr(finalControl+'.'+attrTmp,mdTmp+'.input'+tmpConnectAttr)
    
                # If test, offset the controllers for visual testing:
                if testing:
                    cmds.setAttr(nodeTmp+'.translateX',count+0.5)
                count += 1
    
        # Group the controllers
        eyeRigGroupTmp = cmds.group(controlsList, n = eyeObj+'_eyeDir_grp')
    
        # Position the eyeRig at the eye
        self.parentUnparent (eyeObj,eyeRigGroupTmp)
    
        # Setup aimConstraint for eye
        cmds.aimConstraint (eyeTarget,eyeObj+'_aim',offset = [0, 0, 0],w = 1, aimVector = [1,0,0], upVector = [0,1,0], worldUpType ='vector', worldUpVector = [0,1,0])
    
        # Constrain Eye to "out" control
        cmds.parentConstraint (eyeObj,finalControl, offset = (0, 0,0 ), w = 1)
    
    def parentUnparent(self, parent,children):
        # Use temporary point-, scale- constraints to snap parent tofuture child
        delete( cmds.pointConstraint (parent, children, offset = (0, 0,0 ), w = 1) )
        delete( cmds.orientConstraint (parent, children, offset = (0,0, 0 ), w = 1) )
        delete( cmds.scaleConstraint (parent, children, offset = (1, 1,1 ), w = 1) )
ins = fn()
ins.eye_rig('testX_eyeL','testX_eyeL_tgt',['anim','aim','out'])

2011/7/27 PixelMuncher <pixel...@gmail.com>



--
http://thatboy.me 


Justin

unread,
Jul 27, 2011, 4:15:02 PM7/27/11
to python_inside_maya
Oops accident replied to the wrong person :-)

I formatted this into a gist so its readable:
https://gist.github.com/1110257
> 2011/7/27 PixelMuncher <pixeldr...@gmail.com>

PixelMuncher

unread,
Jul 28, 2011, 12:34:13 AM7/28/11
to python_inside_maya
Thanks much - this gives me something to build upon.
@ Justin:
Why did you specify an 'object' param to be passed to the class?

matthew evans

unread,
Jul 28, 2011, 6:12:52 AM7/28/11
to python_in...@googlegroups.com
the object param creates a new style class

matty


--

Alexander Morano

unread,
Jul 28, 2011, 11:21:29 AM7/28/11
to python_inside_maya
I think I am failing to understand why this had/has to be a class? Top-
down, functional programming ain't dead =)

There is nothing more than another layer of encapsulation on this now,
but no exposed functionality to it.

And I did LOL (sorry) that someone prepended a def class atop this =)

I think my problem stems from the fact you should be initializing the
class with the eyeObj and whatever other information, and then calling
eye.rig()
if you wnated to really start to follow the paradigm of class use/OOP

Cheers.

Justin

unread,
Jul 28, 2011, 3:21:48 PM7/28/11
to python_inside_maya
I completely agree with Alexander in that this exact code didn't need
to be a class. it doesn't really have an internal state or do anything
that really needs initialization.
BUT, I do recognize that what the OP is trying to do is learn the
language, so providing him with an example of his code in class-form
does help him and move him forward. Now that he sees the syntax, he
can start to investigate why it would or would not benefit from being
a class. As you can see, he only has two functions, and one of them
its basically just a static method.

Being someone that write python code in maya on a daily basis for over
2.5 years, I will say this. I write all my scripts as class structures
for organizational purposes. It happens frequently that I want my
scripts to have options for the way they will be executed, and it
makes having a library of script a lot easier, when I want to use one
as a library component of another script. Its pretty rare that my
tools will only be composed of two functions. But its quite MEL-like
to have all functions and the exported ones are global. In MEL you
aren't saving state, you are just passing around arguments.

When I started learning python over 5 years ago, the friend who was
giving me advice at the time suggested that its pretty much always
cleaner to write your programs as a class. If they really are just
some utility operations they can be functions.

Alexander Morano

unread,
Jul 29, 2011, 12:03:42 AM7/29/11
to python_inside_maya
Fair enough.

Though one can make the argument that modules already provide this
first level of encapsulation i.e. wrapping in a "class" for
oganizational purposes.

Especially since modules can also be wrapped behind a namespace.

This one could call a module any "class" they want, and access those
functions as if they were member functions of a class.

I think while learning OO is a positive endeavor that I was kind of
scratching my head on how this was a good method of learning what it
really means; syntax is just syntax.

By no means do I discourage any transfer from MEL to python+pymel. =)

Cheers.

PixelMuncher

unread,
Jul 29, 2011, 1:20:00 PM7/29/11
to python_inside_maya
Justin pretty much summed it up.
I'm not a professional coder, but I write a lot of Maya tools for
myself.
I started w/Mel, then checked out Python, which has become my favorite
language.
I haven't used classes, and want to find out about what I'm missing.
I understand your point of view Alexander - that using classes could
be considered superfluous for my needs, but I want to see how they
work and if I can see any advantage to using them in my coding.
I appreciate Justin's comment that they can used simply as a way to
keep code organized - I have done similar type of coding (using
methods and attributes) with Actionscript, and this worked well for me
in the past.
I used pymel for awhile, but found that, as a one person shop, it was
too hard to keep track of what is pymel vs. what is python, so for now
I'm sticking to python.

Also, regarding the use of the object param to create a new style
class - I read up on that. Is it correct that that is now optional
and all classes are automatically cast as they type 'object'?

Thanks for the help and feedback.

matthew evans

unread,
Jul 29, 2011, 1:31:42 PM7/29/11
to python_in...@googlegroups.com
im not sure in python 3+

but in 2.6 which i preferably use, the answer is no. to create a class of type object or type, you actually have to pass in the object param. 

matty


Martin La Land Romero

unread,
Jul 29, 2011, 2:55:30 PM7/29/11
to python_in...@googlegroups.com

I was also wondering the same thing that pixelmuncher brought up. I usually relay on functions instead of classes. I would love to learn more about their within maya.

Thanks

Justin

unread,
Jul 29, 2011, 3:56:15 PM7/29/11
to python_inside_maya
Some more specific reasons I use classes...
If you want to have it run in a verbose or debug mode, to maybe print
more output, you would make it a class that has say a verbose
attribute. Then all your methods can check "if self.verbose" and do
more output.
Also, say your tool has a UI component to it. I have a lot of tools
that can just be run as a command or can bring up a GUI. You GUI can
be brought up with, say, a show() method and can set the state of
attributes for when the actual work method gets called.

self.show() -> set some attribs: self.thisVal, self.thatVal -> hitting
an ok button calls self.create()

I'm sure that I am missing a lot better examples. But I'm just coming
up with this off the cuff :-)
I have one tool that automatically sets up IK on the legs of a
character, from the selected joints. It has a lot of internal state
where its remembers names and paths of objects its creating along the
way. It would be a lot more work for me to have to pass around lots of
data structures to maintain the state as it runs. I can break work up
into smaller routines that maybe don't return anything, but rather
just update the attributes of the class instance.

Also, if you end up getting into using pyqt in maya (which kicks ass,
as opposed to dealing with that shitty MEL ui code), you will end up
using clases. I wrote a blog article about installing that:
http://www.justinfx.com/2011/01/07/installing-pyqt-for-maya-2011-osx/

As for pymel, I tried it out in maya 2009 before it came integrated in
2011. I like what they were trying to do, by making the calls more
pythonic, but I actually found it to be slower than calling the
standard python API. Probably a lot of overhead in what the underlying
code does to populate its objects. Maybe its faster now, Im not sure.



On Jul 29, 2:55 pm, Martin La Land Romero <martinmrom...@gmail.com>
wrote:
> I was also wondering the same thing that pixelmuncher brought up. I usually
> relay on functions instead of classes. I would love to learn more about
> their within maya.
>
> Thanks

Martin La Land Romero

unread,
Jul 29, 2011, 4:15:33 PM7/29/11
to python_in...@googlegroups.com

Martin La Land Romero

unread,
Jul 29, 2011, 4:18:20 PM7/29/11
to python_in...@googlegroups.com
Justin, Would your tutorial work for Maya 2012 as well?

On Fri, Jul 29, 2011 at 2:56 PM, Justin <justin...@gmail.com> wrote:

Justin

unread,
Jul 30, 2011, 4:33:19 PM7/30/11
to python_inside_maya
Yes as far as I know, the instructions should be the same except for
the version of the packages.
Instead, you would use:
Qt - 4.7.1
SIP - 4.12.1 or later
PyQt4 - 4.8.3

They used a newer version of qt, and I think its now python 2.6
instead of 2.5


On Jul 29, 1:18 pm, Martin La Land Romero <martinmrom...@gmail.com>
wrote:
> martinmrom...@gmail.com
> (415)261-2172

Martin La Land Romero

unread,
Jul 30, 2011, 4:40:21 PM7/30/11
to python_in...@googlegroups.com
Thanks a bunch!

Martin
Reply all
Reply to author
Forward
0 new messages