2010年11月14日

Maya, Mel, QT and You - Interfacing with the QT Designer

After a rather harrowing weekend I decided that I'd follow through on my promise of a Maya/Mel/QT tutorial, so here it is! So without further ado, here we go:

So today we're going to go through how to make a basic qt interface. What we're going to be creating is an interface that will do 4 things:

Get user input to name an object 
List all the user objects 
Use a combo box to create an object 
Rename an object from the list box 
The basic premise of this interface will be to make a UI for a user to make an object, with a specific name. It will also list all geometric objects in the scene so we can select and rename the objects.

What we need:

Well to continue with the tutorial to test it out, you'll need a working version of Maya 2011. You can download a trial version from Autodesk's website if you don't already have one. You'll also need the QT toolkit which you'll need to download from Nokia's website. Download the LGPL version, unless you really want to distribute your apps in some other way. (I personally love Open Source software, so that's why I distribute them under a license that is Open Source friendly.) You're also going to need a text editor of some sort. If you're using Windows, I would suggest Notepad++ with the Mel language definition file from CreativeCrash.com.

If you're using a Mac I would suggest either Text Wrangler, Carbon Emacs or if you're familiar with it, Vim. If you choose the latter of the three, I will assume that you are familiar with Vi or are willing to go through the learning process for it. While it has a learning curve, it is EXTREMELY powerful. Emacs is just as powerful as Vi, and I do NOT wish to start that war on here.

If you're using Linux, I'm going to assume you: A) Know what you're doing, since you're running an OS that requires that; and B) Have a text editor such as emacs, vi or some other in mind. However, just for the sake of being thorough, here are 2 choices other than Vi, Emacs or Nano. Nedit has an .edit preferences file on CreativeCrash for syntax highlighting for mel, so that's a useful editor. You can also use jEdit and use this Configuring jEdit for Maya tutorial to get yourself up and running.

As a note, everyone can use jEdit on every platform if they want to since it's built off of Java. I tend to stay away from Java based apps because they're (generally speaking - I'm not knocking java developers) heavier on memory usage, however, jEdit is a VERY powerful editor, has syntax highlighting and code completion, so if you're looking for a great free editor and need cross platform availability, then jEdit is for you.

If you are looking for a very feature rich text editor for Windows, and don't mind spending a bit of money, then I'd suggest E Text Editor. if you're on a Mac, I would suggest Textmate. Both have syntax highlighting for Mel built in (they use the same libraries), so that for me was a huge selling point. They both also have a 30 day free trial, so try before you buy (you don't want to waste the cash if you don't like it).

Now that I've given you way more stuff to choose from (there's a lot to be said about being thorough), let's get started. Since this tutorial is focusing on the creation of the interface and hooking it into the mel code, I'm going to give the code for the creation of everything right here. Save that as whatever you'd like with the extension .mel. I'll be naming mine object_creator_tutorial.mel . It made sense to me. 

        global proc objectCreator(){
                //If Maya's version is greater than or equal to 2011 go ahead
                if (getApplicationVersionAsFloat() >= 2011){
                        //set the scripts directory so we can load our ui file
                        string $scriptsDirectory = `internalVar -usd`;
                        //load the ui file
                        string $objectCreatorWindow = `loadUI -uiFile ($scriptsDirectory+"/object_creator_tutorial.ui")`;
                        //show the window
                        showWindow $objectCreatorWindow;
                        //find all objects in the scene and load their mesh transform name
                        updateObjectsInScene;
                }else{
                        //You aren't using Maya 2011 if this pops up. The upgrade is worth it! 
                        print "You're using a version of Maya that is incompatible with this script.\n";
                }
        }

        global proc updateObjectsInScene(){
                //to get the meshes available
                string $listObjects[] = `ls -type mesh`;
                //get the transform node
                string $topLevelListObjects[] = `listRelatives -parent -type "transform" $listObjects`;
                //clear the scroll list so no duplicates show up
                textScrollList -e -ra objectScrollList;
                //iterate through and add all the meshes
                for($item in $topLevelListObjects){
                        //update the items in the scroll list
                        textScrollList -e -a $item objectScrollList;
                }
        }


        //change the object(s) name
        global proc objectRenamer(){
                //select all the items that are active in the list
                string $sel[] = `textScrollList -q -si objectScrollList`;
                //get the users input for the new name
                string $objectNewName = `textField -q -text objectNewName`;
                //rename the object
                rename $sel $objectNewName;
                //deselect everything in the scrollList so we don't change things we don't want to next time
                textScrollList -e -deselectAll objectScrollList;
                //clear all selections
                select -cl;
                //update the scroll list since we just renamed the object
                updateObjectsInScene;
        }

        //create the object
        global proc objectCreation(){
                //get the object we want to create        
                string $createObject = `optionMenu -q -v typeOfObject`;
                //get the name of the object we want to create
                string $objectCreateName = `textField -q -text objectNewName`;
                //create the object
                eval $createObject;
                //get the selected object
                string $sel[] = `ls -sl`;
                if($objectCreateName == ""){
                        //do nothing
                }else{
                        //rename the selected object
                        rename $sel $objectCreateName;
                }
                //update the scroll list since we just created an object
                updateObjectsInScene;
        }
Alright. You've got that saved and you've got everything running. What we're gonna do now is launch the QT Designer. When it launches it will look like this:


We want to choose "Main Window" from the templates/forms sidebar and then click create. A window will pop up that looks as such:


What we're going to do from here is this (I'm not going to provide screenshots for every single nitty gritty detail as most of it should be self explanatory):

Resize the window to how large or small we want it 
Add a layout to contain our objects 
Add a container to contain our objects 
Add in our 8 objects that we're going to create 
A list widget 
A combo box 
A line edit 
2 labels - one for the line edit and one for the combo box 
3 push buttons 
One for creating our object 
One for renaming our object 
One for reloading the list boxes contents 
Attach the necessary code to each object 
Add in controls to get the data 
It's a fairly long list, but we should be able to accomplish all of this in roughly an hour or two. Alright. Let's get started.

First we're going to click and drag a grid layout from the widget box panel -> layouts to our working area on our layout. Resize it so it's large enough to fit all of our objects (you can tweak it later if it's too small. Now grab a container of some sort (I'm going to use a group box) and drag that into the grid layout. Now we can go ahead and drag all of our objects into that layout. Now one note I would like to make is that when you bring your List Widget over, make ABSOLUTELY sure that you use the List Widget under Item-Based, NOT Model-Based. If you use Model-Based you will never see your results on the screen. You can position them and resize them as you see fit. Here's how mine looks:


As I'm sure you've noticed, I've named my objects already. If you haven't gotten to this point, that's alright, I'm going to go through it for each object. If you have, you can skip this part (I'll put a notice as to where it's pertinent to you again). You may however may want to read this paragraph and the next one so you know what options to set where. The first thing we'll want to rename is our Main Window. To do so first select the Main Window in the Object Inspector. Then in the Property Editor scroll down until you see the word window on the left hand side. Simply click in the field next to it and enter in the name you wish to use. Here's a screenshot of that:


Readers that skipped ahead this part is for you too. Now let's rename our list box and set some options for it. When you select it at the top it should say objectName. We want this to match our mel script, so we'll name it objectScrollList . It should now look like this:


Now let's also change a few options. We want to change the edit triggers from what it currently is to No Edit Triggers. So twirl down the options and double-click the top option that says No Edit Triggers. At this point we can be done editing this, but if you want you can allow the user to drag and drop the objects to re-order them. To do so make sure that dragEnabled is checked, then change dragDropMode to internalMove and defaultDropAction to MoveAction. You can also set alternating row colors, but it doesn't show very well unless you make a custom color scheme for your ui. Keep selection mode at SingleSelection as well. At the bottom you'll see an option for sorting called sortingEnabled. This is useful, but if you want the drag and drop ability make sure this is NOT checked.

Readers that skipped ahead you can continue skipping again.  Ok, now let's modify a button. Select any of your buttons. At the top you can rename it to whatever you'd like with the suffix of Button. For mine, I've renamed it to renameObjectButton (with the same naming scheme for the rest of them). Now scroll down to where the list turns green (QAbstractButton) and find the text field. Click that field and type in your name. Done. That's all we're doing for that. You can rename the rest of the buttons in the same manner.

Readers that skipped ahead this part is for you too. After you're done with that we need to give an object name to our line edit field. We're going to name it objectNewName, since that's what's in our script. We're done with that now.

Now we need to modify the combo box. Now for this we're going to give a bunch of really ugly names. The reason for this is because we need to get the actual names of the object creation. We could name them differently and setup a bunch of if else statements, but for this exercise we want to keep it simple. So double click the combo box and click the plus sign at the bottom. Type in polySphere. We're going to do this several more times, so here's the list of names you need to put in.

polyCube 
polyCylinder 
polyCone 
polyPlane 
polyTorus 
polyPrism 
polyPyramid 
polyPipe 
polyHelix 
Readers that skipped ahead you should continue from here. We've omitted the soccer ball and the platonic solids because the code for creating those is ugly and so it looks worse than these already do.  Alright. We've got our combo box with stuff in it. The only thing left to do is name it. So in objectName, name it typeOfObject as per our mel.

At this point we're almost done! Now all we need to do is connect a few functions to the buttons. So select your Reload Objects button and go to the property editor. Click the big plus sign and select string. Now type in -command in the pop up. Now a new object in the property list will show up under dynamic objects. Next to -command type in the text field updateObjectsInScene . Now select the create object button, add the -command again and type in objectCreation . Now select the rename object button, do the same as before and type in objectRenamer in the text field this time. We are now done! You can drop both the mel file and the ui file (after you've saved it of course) and put them in your scripts folder! To see it in action, just type:

source object_creator_tutorial.mel; objectCreator();either into the mel command line or into the script editor and run it! This is what you should see:


I've created a few objects using it and renamed the sphere to test. Nothing fancy. Just a way to learn QT and Mel. 

But wait! How are you going about pulling that information from those fields? Great question self! I'll go through what commands I'm using to pull the information from all of those fields. The code is commented, but again, I like being thorough.

Ok so, to name our object, I chose to use a single text field rather than using two, because of two reasons: 1) It cuts down on code; and 2) It makes it so the interface looks cleaner. Remember when we named our text fields top level name to objectNewName? Well this line right here pulls that name:

string $objectNewName = `textField -q -text objectNewName`;Maya understands that the UI has a textField and it's name is objectNewName. The -q flag queries the data from that field with -text being the explicit data we're looking for. Note that maya treats numbers as text as well (because QT doesn't have an integer box, only text boxes), so if you need an integer from a text field, simply recast it doing something like this:

                        string $textFieldData = `textField -q -text nameofTextField`;
                        int $textFieldNumber = $textFieldData;
                Sorry for the aside, but knowledge is power.  Ok. So we got the text via that line. How are we putting stuff into the scrollList. This actually took me a few minutes to figure out because of two reasons: A) I followed someone else's documentation without trying for myself; and B) It's the only object to be Item Based. So we named our Scroll List to objectScrollList . Here's the code that's updating that list:

                        //to get the meshes available
                        string $listObjects[] = `ls -type mesh`;
                        //get the transform node
                        string $topLevelListObjects[] = `listRelatives -parent -type "transform" $listObjects`;
                        //clear the scroll list so no duplicates show up
                        textScrollList -e -ra objectScrollList;
                        //iterate through and add all the meshes
                        for($item in $topLevelListObjects){
                                //update the items in the scroll list
                                textScrollList -e -a $item objectScrollList;
                        }
                        
                What we're doing is querying the scene for any meshes and then selecting their transform nodes rather than their shape nodes (which would be the default). Then we clear the scroll list so we don't add duplicates (just being sanitary). After that we go through each item and append it to the current list. We could add a function in here to check and see if there's any objects in the scene and if not to not bother running the function, but that'd be an extra variable and an if else statement, so that's another 6 lines of code. While it's good to add in error checking and I usually do, the script would probably run slower if it had that ability since it'd be another iteration. However, we can add that if we want to later. Again, this is more of a demo than a mel howto.

So now we know where we're getting our variables from and what commands are pulling the data. What's left? Absolutely Nothing! Now you're equipped with a better idea of how Mel, QT and Maya work together! Hopefully this was informative to you and you can get out there and write some awesome scripts using QT. If you'd like to know what other commands interface with Maya from QT you can check out this post on CreativeCrash from css_maya. He does a great job of outlining the different variables, although you will note that his list view is incorrect (which you'll also see I pointed out in his comments). He also goes through setting up a dial control to work with Maya. I'd rather use a slider, but to each his own! It's cool and you can do it so why not!

If you need any help, feel free to shoot me an email from my contact page or if you're reading this on CreativeCrash send me a pm! Have a good one and make some cool stuff! Oh and you can download the files for this tutorial here.

沒有留言:

張貼留言