Maya Python 101: Making Your Own Presets Tool and Settings Database
When I made the V-Ray Tuner Presets feature of V-Ray Tuner, it became apparent that one of the handiest things you can do in scripting is give people a personalized method of storing commonly-used settings. So I thought I'd make a post to show how easy it is to do in Python in Maya. As an example, I managed to make a decent little pose Manager in under 150 lines of code:
Grab Poseshot here. The script is not super complicated and can be understood by most people who know how to write a for loop and do basic Python string manipulation like split and join.
First, let's break down our Poseshot utility into its different components:
A prompt for a text string to store as the pose name
A text file we store our values in and the command to write it
An optionMenuGrp dropdown menu that dynamically populates with our presets
A portion of the script that reads the text file and loads that as a command
Don't worry – you don't need to understand SQL or anything to do this. It's really just a matter of figuring out a way to write data out in a way that can be parsed easily later. For Poseshot, I have a couple things I want to store: a name for the pose and the rotation and translation values of all selected curves that make up the pose. This seems like a big task but it's actually really easy once I explain the rationale behind my pose database text formatting:
The __, ??? and !!! characters serve a purpose: they act as delimiters – text that is not likely to appear in your saved names or stored values so they can safely be used to say "okay, before __ we know that we have our pose name so..." I've used ??? as the delimiter for my stored curve names and float values and !!! is to indicate I'm at the end of the individual pose data. If you have a habit of enthusiastically writing !!! after your pose names, you're gonna have a problem with Poseshot so I recommend chilling out or using a different delimiter. What's also important in the text above is that the single space between the name of the curve transform and the float value is unique (single spaces don't appear elsewhere). So, with this in mind, we can now write a pose for the selected curves with a promptDialog that sends the text you enter to our store method. Here is that portion of the script with some extra sanity checks removed for simplicity – I've explained each line in comments to the right. If the image is too small, open it in a new window - it's high res:
That results in a single entry like our headturn pose above. The database itself doesn't care how many entries it has – they can all be of different length and we aren't going to choke trying to set values we haven't explicitly entered in our text file.
There is also a logic to the way I've written my curve transform name and then the values. You can save yourself some work by writing your objects to be manipulated and the values in a way that can be read easily as Python code later. For example, instead of writing a bunch of values and stuff that has no connection to anything, forcing you to sort out that stuff later, write your file so it's easily parsed back into a command with no ambiguity about what the object is and what will be done to it. So, for our finger3FBXASC0451FBXASC046L_CTRL rotation in the x, store it and the float value in a way that resembles the setAttr command to apply it later:
cmds.setAttr('finger3FBXASC0451FBXASC046L_CTRL.rx', 34.0)
That's the command we want to apply later to pose finger3FBXASC0451FBXASC046L_CTRL. So here is what I'll write for the entry in the database:
finger3FBXASC0451FBXASC046L_CTRL.rx 34.0
When you read back your text from the file, parse it so you are left with a command to execute as Python code. So when we read our database, here is the way we do that:
Now that we know how to write our database, read it back and execute, let's look at building the optionMenuGrp from our text file pose name entries.
Building a dynamic dropdown menu
This is one of the more satisfying scripting elements to build because it feels like magic when it works for you the first time. It is actually pretty simple in concept: make an empty place in the GUI portion of your code to hold the optionMenuGrp and then use a separate function to build it. Here's what that section looks like in the GUI code:
So you've create a columnLayout that's sole function is to hold the optionMenuGrp. Then we call the method to build it and then we promptly go back up a level in the GUI – done with setParent('..') – of that section of the GUI so nothing below it is mangled by the dynamic width of this element.
Now let's look at the buildposestoremenu method:
That's pretty much it. Now you know how to harness the power of presets.