PyQt Stylesheets, Maya, and Python Oh MY!

Written by mattanimation. Posted in Maya, pyQT, Python, Tutorials, UI

Ok, so you have some tools built and you have a basic understanding of Python and Maya but you want to start customizing things a bit more, maybe the ‘plastique’ style UI just isn’t as cool as it used to be and you want your tools to have a uniform or unique style to them. This is where stylesheets come into play.

No doubt if you have searched Google you have found the post in TAO that Yasin Uludag started where he released his “darkorange” stylesheet. This is a great resource to get started, but I think there are some details still missing on how to get it setup and working so I decided to make a simple tutorial on how to get a basic application started. This tutorial features the following:

  • How to create a simple PyQt application
  • How to create and compile a .qrc file for icons
  • Edit Yasin’s stylesheet to create your own style
  • Have the application set some options to send to mayapy and create a maya file
  • maybe create your own app to compile .qrc files to .py files easier
Part 1

Part 2

QRC Code:

<RCC>
  <qresource>
    <file>images/checkbox.png</file>
    <file alias="down_arrow.png">images/down_arrow.png</file>
    <file>images/handle.png</file>
  </qresource>
</RCC>

Shell Script:

pyrcc4 icons.qrc -o icons.py

Python:
References:

http://nathanhorne.com/?p=451
http://lateral.netmanagers.com.ar/stories/BBS49.html

Rigging Dojo Week 3

Written by mattanimation. Posted in Maya, pyMel, pyQT, Python, Rigging

So during week 3 it came down to finish up the base skinning and figuring out how I was going to tackle the corrective shapes and/or helper joints. My mentor showed me a video of some tools he uses that I found to be very useful so did my best to re-create them. While doing so I came up with these two tools.

Tools

  • Skin Weight Slider – I really just wanted to get out the idea that was in my head. I have seen nudge tools in the past, but as I was working I visualized a graph with colors to represent the weight values, and sliders to control them, the sliders would update as other influences changed. In a way it is just more simplified and interactive component editor. Still needs some tweaking to make the calls to get the weights smoother. It also has a button to show the influenced verts of the selected joint.
  • UV Pose Driver  – This one is like a simplified PSD tool. It creates a shape on a joint that reads the point on surface where the joint above is in relation to, by keeping a 0-1 value the info from the uv co-ords can be used to drive a corrective shape. Still updating this one as I go as well. The way the tool works is:
    • Select a blend shape from list (updates blend target list)
    • Select a blend target
    • Give pose a name
    • Create it, then position it at the angle where you want the values to be driven.
    • rinse and repeat.

 

Using PSEXEC

Written by mattanimation. Posted in Maya, Python

ps- what?

What the heck is psexec you say? An admin tool from Microsoft for windows users (barf I know, but its what we got right?) that can be found here . OK? so what can it do? Well haven’t you ever wanted to send commands to another computer on your network? If not just to mess with people? This tool lets you run commands through the console to other machines on your computer.

Example

Let’s say you want to log into another computers cmd console, you would first need to

  • download psexec, a simple small .exe file
  • open your console…cmd
  • cd to the dir you saved psexec in

Now you are ready to get started. Here is a simple example of the command:


psexec \\TARGET_COMPUTERS_NAME -u username -p passwrd command stuff here

or in real case it might be something like this


psexec \\my_precious -u bilbo -p sting cmd

Something like this would result in the command console appearing to be the command console on the other computer,  from there you can run all the commands you want just like normal. Where this becomes more useful is being able to take that same command and have it run from say….python….in Maya. I recently worked on a tool that needed some functionality to select certain combinations of files, then reference them into new files, then those files were but in batch files that were then dived up between render machines, but how would you get those machines to start those renders from Maya?

By having the batch files located in a networked location the file name can be sent to each machine as an argument from psexec. Now you may say, why not use Afterburner? or what is this hackery? Well the point is, we cut the middle man out, why do I want to muck around with afterburner after the fact and spend more time when I can have it all done with one click?

See what else you can use it for!

Black Box Animation Toolset

Written by mattanimation. Posted in Maya, pyQT, Python, Rigging, UI

Well I figured I should just go ahead and start displaying what I have been working on in my secret lab the past little while in the hopes that putting it out there will actually make me finish the thing. Also to post a history of my workflow and some tips about things I have learned on the way. What is this “Black Box” you say? At first it was to be my custom modular rigging tool, and now it has become a mini character animation pipeline so to speak. The idea behind the name is to have a tool that plugs into everything so the modules are built in a way that allows changes to be made to the rig after it is deemed “complete” which never happens which is why a feature like this is important. Some features include:

  • Modular character rigging
  • Template saving and loading
  • custom character GUI layout built in
  • pose library built in
  • other TD tools for common tasks that tie into the rigs

Aside from this I plan to add a facial rigging portion dubbed “Gray Skull” that will allow for a standardized joint based rigging approach for most characters. The final tool I plan to add to this dubbed “Chop Shop” is a modular vehicle/mechanical rigging portion that will be used for..well…vehicles and stuff, it will focus more on dynamics and plugin development.

This is way too much for one to handle! Why are you doing this dummy, it has been done before!? Originally this whole thing was thought up a few years back when buddy of mine and some other friends from school were going to make a short film. I had started a rigging tool and then that dream was abruptly cut short after loosing my job (darn you federal reserve and bailouts that cause layoffs!) so it had to go on hold while I acquired some kind of vocation to support the fam, but enough sob story. I heard more about these ‘modular rigging’ things and found out, hey I need to learn Python since MEL ain’t gonna cut it anymore (although still alive just not useful outside of Maya). Needless to say, the root of this comes from a DVD by Steve Twist that is a doozy for a beginner at Python but I had somewhat of a coding background so it wasn’t too crazy, just time consuming. As of recently I have been slowly coming to the conclusion that the current system in place needs a lot of work although it works for now, much more is to be done. I think I want to convert it to PyMel as I have not yet made that jump, no time better than now right? Basically I want a tool that allows me to be my own little studio if a decent size freelance comes up I can handle it. I’ve even got a Kinect now and plan on adding a mo-cap layer in there somewhere.

So! On to the learning stuff, first stages:

  1. Think About it sit down and figure out what you are trying to accomplish and make goals
  2. Make a features list list all the “wish list” items you can think of, even if you have no idea how to do it
  3. Always Sketch the UI Before Implementation, show it to others before you get started for feedback as well.

Here are my initial sketches part of what it looks like in Maya so far:

One thing to note is all of this is still subject to change. Don’t limit yourself to just one design because it might be more work to redo it. Which brings me to my next point…keep your UI separate from your main code and other modules, this way if there IS a change then it will only affect this one file since the functionality should not be affected by the users interaction with the interface. I think this a good first post, more to come.

Deformer Sets in Maya

Written by mattanimation. Posted in Maya, Rigging, Tutorials

I had a project recently that led to me discovering a useful but often overlooked (by me) menu known as deformer sets.

As an example let’s say you have a ball that is deformed by a lattice. Then you need to have another ball deformed by that same lattice, well that’s what the deformer set menu is for.

ds_01 ds_02

It can be found under Window>Relationship Editors>Deformer sets.

ds_03 ds_05

To get the now unaffected sphere to be affected by the lattice, simply select the ffd set for that lattice in the left column and then the object you want to be influenced by it in the right.

ds_06

Now the object is affected by the lattice. (The image looks weird because the sphere isn’t  sitting in the center of the lattice).

ds_07

Okay, now what if you only wanted a specific part of that object affected by the lattice? You can use the Edit Membership tool under the animation set in Edit Deformers> Edit Membership.

ds_08

Make sure you have the lattice (or whatever other deformer you are using) selected. Hold down shift then use the cursor to select the verts you want to add or use ctrl to deselect them. Now you can see that the specific area that was selected is being deformed. That’s all there is to it!

ds_09 ds_10

Some Qt Tips

Written by mattanimation. Posted in Maya, pyQT, Python

I’ve been jumping back into using Qt recently, this time in Maya and although there are many resources out there to help get started (I will be posting some below) there are a few things I ran into that might help others on the way.

  • Scroll Areas

QtGui.QScrollArea  this is interesting, even if you are in Qt Designer you can’t seem to get a preview of it working. To get this to work you need to first make a widget and a layout, set the layout of that widget to the newly created layout. Now create the QScrollArea and there is a method called “setWidget”, you want to pass the widget you first created into this method. Next you should add the scrollArea to whatever layout it’s going to be sitting in. When you want to add something to the scroll area you need to add it to the layout of the widget you first created. A little strange but makes sense once you do it once or twice, see code below.


# create a scroll area for the modules to load
myLayout = QtGui.QVBoxLayout()
scrollingWidget = QtGui.QWidget()
scrollingWidget.setLayout(myLayout)

myAwesomeScrollArea = QtGui.QScrollArea()
myAwesomeScrollArea.setWidgetResizable(True)
myAwesomeScrollArea.setEnabled(True)
myAwesomeScrollArea.setMaximumSize(375, 300)  # optional

myAwesomeScrollArea.setWidget(scrollingWidget)
scrollParentLayout.addWidget(myAwesomeScrollArea)

# add item to the scroll area
myLayout.addWidget(widgetIWantToAdd)

  • Button Click Methods

Let’s say you wanted to have a method run when you click a QPushButton, you would write something like this:


myBtn = QtGui.QPushButton('Click Me')
myBtn.clicked.connect(someMethod)

def someMethod(self, *args):
    """
    some comments
    """
    print 'something'

That works just fine and I’m happy with that. Now what if I needed to pass some values to a method when that button is clicked? If you try to add them like this:


myBtn = QtGui.QPushButton('Click Me')
myBtn.clicked.connect(someMethod(arg_1, arg_2))

def someMethod(self, knight, phrase, *args):
    """
    some comments
    """
    print 'ni!'

You will get an error! You do not want this guy –>    hanging around your code, believe me. So how to remedy this troublesome citizen? We have 2 options, “partial” and “lambda“. Partial is a module that needs to be imported from functools and it allows you to specify a method call followed by the arguments you wish to pass it all in a single line. It runs as a single function thereby letting the button do it’s thing and you can continue on your merry way. Lambda? what is this lambda you speak of? In a nutshell its a syntax in python that lets you create a function on thy fly, so we can can create a quick single function that just runs our method call with arguments passed inside. Here are your jolly examples.

using partial


from functools import partial

myBtn = QtGui.QPushButton('Click Me')
myBtn.clicked.connect(partial(someMethod, arg_1, arg_2))

def someMethod(self, knight, phrase, *args):
    """
    some comments
    """
    print 'ni!'

using lambda


myBtn = QtGui.QPushButton('Click Me')
myBtn.clicked.connect(lambda : someMethod('brave sir robin', "I don't know that"))

def someMethod(self, knight, phrase, *args):
    """
    some comments
    """
    print 'aaarrrggg!'

Here are some other links to get started using PyQt in Maya:
Kristine Middlemiss

David Coleman

There are more links in older posts as well.

Using JSON to store node information in Maya

Written by mattanimation. Posted in JSON, Maya, pyMel, Python

Recently I have been looking into ways to optimize a pose library that I made. The pose information was stored using XML. This worked great and was all dandy, but my implementation always seemed a bit clunky, having to open and close the file read write and change the information, mainly having to traverse a hierarchy then store it in as a list to make changes before re-writing the pose. I read that others have used JSON which had heard of many times and since pretty much every coding language out there has support for it, it must must be a win right? I gave it a shot and came up with this example today:

import json
import pymel.core as pm

def store(name, objs):

    pose={}
    ctrls=[]
    for o in objs:
        data={}
        for attr in pm.listAttr(str(o), keyable=True, unlocked=True):
            value = pm.getAttr(str(o)+'.'+attr)
            data.setdefault(str(attr),value)

        ctrls.append([str(o),[data]])

    pose.setdefault(name, {"ctrls":ctrls})

    f=open('/Users/yourname/Desktop/' + name + '.rp', 'w')
    f.write(json.dumps(pose))
    f.close()

def load(poseToLoad):
    f=open('/Users/yourname/Desktop/' + poseToLoad +'.rp', 'r')
    data=f.readlines()
    f.close()
    j_data = json.loads(data[0])

    ctrls = j_data[poseToLoad]['ctrls']

    for c in ctrls:
        ctrl = c[0]
        attrs = pm.listAttr(ctrl, keyable=True, unlocked=True)
        for a in attrs:
            at = c[1][0]
            val = c[1][0][a]
            pm.setAttr(ctrl + '.' + a, val)

if len(pm.ls(sl=True)):
objs = pm.ls(sl=True)
#   store("pose1",objs)
load("pose1")
Explaination

In the block above there is an example of using JSON to save the attributes of the selected nodes in maya, the node names, and a pose name associated with it. You can paste this code in a python tab in Maya and run it(make sure you insert your own file path where needed) to see the results. Basically JSON uses a few common data structures that easily recognizable even to the beginner python user, they are: dictionaries, lists, strings, and numbers. At first the structure looked way harder to read than XML but after a second glance it literally was just looking at a nested dictionary or list, and then I realized something even cooler, when I read the information back from the file, that’s exactly what they were coming in! No more traversing elements and storing into local lists, just a simple clean load right into the pyMel commands.

Now the thing that I am still working on is the actual data structure and what would be most desirable to work with, this works for now, but it would be awesome to just reference everything by the key and not worry about the indices of an array. Needless to say it’s worth looking into if you haven’t already.

*Update*

After actually implementing this and testing with my pose library I was able to shorten the code significantly. The data structure itself now looks like this:

{"poseNameHere": {"ctrls": {"pCube1": {"translateX": 2.5280296598674887, "translateY": 1.4508343627372717,
"translateZ": 11.414172610952747, "scaleX": 1.0, "scaleY": 1.0, "visibility": true, "rotateX": 0.0, "rotateY": 0.0,
"rotateZ": 0.0, "scaleZ": 1.0}, "pSphere1": {"translateX": -8.2447907269338465, "translateY": -1.2861870433449312,
"translateZ": 12.463030765724765, "scaleX": 1.0, "scaleY": 1.0, "visibility": true, "rotateX": 0.0, "rotateY": 0.0,
"rotateZ": 0.0, "scaleZ": 1.0}, "pCylinder1": {"translateX": -6.3732975936497738, "translateY": 2.1936347842880819,
"translateZ": 7.2561024617979939, "scaleX": 1.0, "scaleY": 1.0, "visibility": true, "rotateX": 0.0, "rotateY": 0.0,
"rotateZ": 0.0, "scaleZ": 1.0}}}}

you might be like wtf is that? Let’s break it down. As you might already know a dictionary is created using a key and a value, they are separated by a colon between them.

 myDict = {"keyName":value}

If I want to get the value, I refer to the key like this:

newVar = myDict["keyName"]

Now looking at the above example again we can see that the poseName’s value is actually a dictionary, that dictionary has a key called “ctrls”. The “ctrls” dictionary holds all the controls for the pose. Each ctrl name is a key as well, and thier value is a dictionary that has attributes for the key and the attr values for the values. Make sense?

Like I stated in the original post, I was working on the data structure, before I had arrays for the ctrls but I wanted to not have to reference an index and just use the names as keys for everything so here is what the read and write functions look like now:


def writePoseFile(self, objs, path, thumb, name):
    """
    Write a file that stores all the keyable attributes on a list of objects passed in.

    @type alist: list
    @param alist: the list of objects(ctrls) to iterate through and get the attr:value pair
    @type string: str
    @param string: the path that the file will be written to
    @type string: str
    @param string: the thumbnail image name
    @type string: str
    @param string: the name of the pose to be stored

    """
    ctrls={}
    for o in objs:
        data={}
        for attr in cmds.listAttr(str(o), keyable=True, unlocked=True):
            data.setdefault(str(attr),cmds.getAttr(str(o)+'.'+attr))

        ctrls.setdefault(str(o),data)

    pose = {name:{"ctrls":ctrls}}
    f=open(path, 'w')
    f.write(json.dumps(pose))
    f.close()

def setPose(self, fpn, *args):
    """
    Read a pose file and set the ctrls attr's values

    @type string: str
    @param string: the ui path to the button that was clicked

    """
    #get the name of the button clicked
    poseName = cmds.shelfButton(fpn, q=True, ann=True) #annotation of btn
    currentTab = self.tabList[cmds.tabLayout(self.UIElements['mainTabLayout'], q=True, selectTabIndex=True)-1]
    poseFilePath = self.currentCharDir +'/'+ currentTab + '/' + poseName +'.pose'

    #read the pose file
    f=open(poseFilePath, 'r')
    j_data= json.loads(f.readline())
    f.close()

    for c in j_data[poseName]['ctrls'].keys():
        attrs = cmds.listAttr(c, keyable=True, unlocked=True)
        for a in attrs:
            cmds.setAttr(c + '.' + a, j_data[poseName]['ctrls']1[a])


    #end