Ok, now that we have the basic layout we can start fixing the parts one by one. I will start with the
textScrollList. I want this control to list selected objects. Now, we have already talked about the command we
have to use here "ls". But how are we going to get this command to throw values into the textScrollList. We
create a proc for this, see code below (I had to put the code in a table to make it more readable. If you
wish to copy the code, use the code with a white background below.):

 
global proc ma_multiAttrEdList ()
{
string $ma_selected[] = `ls -sl`;
  if (!(`size($ma_selected)`))
{
warning"nothing selected. List will not display any objects";
}

else
{
textScrollList -e -ra ma_multiAttrEdTSL;
for ($ma_sel in $ma_selected)
    {
string $ma_pickWalked[] = `pickWalk -d down $ma_sel`;
textScrollList -e -a $ma_pickWalked ma_multiAttrEdTSL;
}
  }  
}    
     
 

global proc ma_multiAttrEdList ()
{
string $ma_selected[] = `ls -sl`;
if (!(`size($ma_selected)`))
{
warning "nothing selected. List will not display any objects";
}

else
{
textScrollList -e -ra ma_multiAttrEdTSL;
for ($ma_sel in $ma_selected)
{
string $ma_pickWalked[] = `pickWalk -d down $ma_sel`;
textScrollList -e -a $ma_pickWalked ma_multiAttrEdTSL;
}
}
}

Now this proc has a lot of actions, it includes an "if statement" and a "for in loop". So now we get to see
how this really works. I'm also going to explain the commands.

  • global proc ma_multiAttrEdList () : This is just the name of the proc, I have no arguments here.
  • string $ma_selected stores the selected objects in an array.
  • if (!(size($ma_selected)`){...}: I check if the size of $ma_selected is NOT. If it's not, it has to be "0". Then inside the curly braces I have a print command telling the user that nothing is selected. We need
    a selection for this script to work properly.
  • else: This is what will happen if we have a selection.
  • textScrollList -e -ra ma_multiAttrEdTSL: First we want to empty the textScrollList in case there were
    already a selection present. If you have something listed there already and you appended another selection it would simply add to the bottom of the list. What we want is the list updating with the new selection and therefore we have to empty it first and then add the new selection.
  • for ($ma_sel in $ma_selection) {...} Here we tell Maya that we want to do something to each object in
    the selection. Of course I want to append each object to the list. Well, not really, I want the SHAPES
    to be listed because they are the ones being manipulated when we change the renderStats.
  • string $ma_pickWalked[]: There is a command called pickWalk. If you check the documentation it
    has certain directions and the one we are looking for is "down". listRelatives could probably also do the
    trick. Try it out if you want to.
  • Last thing is an edit of the textScrollList. We want to append the shapes which is stored in
    $ma_pickWalked and the layout being edited is ma_multiAttrEdTSL.

That's basicly all the proc does. Now I'm going to do two things with this proc. First I'm going to put it in the
command for the button and then I'll put it somewhere in the code between "window" and "showWindow". This
way the list will be updated if the user has a selection when he/she opens the window. The changes are in bold
letters:

if(`window -ex ma_multiAttrEdWin`)deleteUI ma_multiAttrEdWin;

window -t "final3 Multiple renderStatsEditor" -wh 285 295 ma_multiAttrEdWin;
columnLayout;
rowColumnLayout -nc 2 -cw 1 154 -cw 2 122 -h 230;
frameLayout -l "objects:" -fn "smallFixedWidthFont" -bs "etchedIn" -h 232;
columnLayout;
textScrollList -w 150 -h 190 -ams 1 ma_multiAttrEdTSL;
button -l "update selection" -w 148 -c ma_multiAttrEdList;

setParent..;
setParent..;
frameLayout -l "options:" -fn "smallFixedWidthFont" -bs "etchedIn" -h 200;
columnLayout ma_optionsColumn;
ma_multiAttrEdList;
showWindow ma_multiAttrEdWin;

You can try it out. If you click the button after selecting some objects the list gets updated. Funny stuff huh? :)

Now the tricky part begins. I want to build the checkboxes and I also want to create a button that changes
the renderStats... Well, you might think. That shouldn't be a problem. You can just add a lot of checkBoxes
and create integers that check the values and then you can just let the button go through all that?

Well, I don't want to use a lot of lines for this and I also want it to be easy to remove or add checkBoxes.
Therefore I create an array that holds all the info I need to create the checkBoxes and the command for the
button. Then there's some mixin' and trixin' to nest all this together. I'll write down the code and go through
it step by step.

//Array that defines the commands I want to use
global string $ma_meshArray[] = {"castsShadows" , "receiveShadows" , "motionBlur", "primaryVisibility" ,
"smoothShading" , "visibleInReflections" , "visibleInRefractions" , "doubleSided" , "opposite"};

I know which attributes the renderStats hold so I simply create an array that can store all of these. Now I can use these as labels as well as commands. ( Because I do it like this the labels will not have the EXACT same
name as in the attributeEditor.) The way I found out which strings to write down was simple. I turn "echo all
commands" on in the scriptEditor, select an object, open the attributeEditor and check/uncheck stats. Then I can
read directly in the scriptEditor what Maya changes. Notice that I declared the array as a GLOBAL string. This is
because I want to use it later inside another loop and if I want to use strings like that I have to make them
global.

//A loop building all the "options" - UI
for ($i = 0; $i < `size($ma_meshArray)`; $i++)
{
checkBox -l $ma_meshArray[$i] -v 1 -p ma_optionsColumn ($ma_meshArray[$i] + "checkBox");
}

You should be able to read this now. This is very close to the example I used earlier, only the command here
is different. The loop checks the size of the array ( which is 9 (count the strings in the array above) ) Let's say
we go through the loop with the first string in the array as an example "castsShadow".

checkBox -l "castsShadow" -v 1 -p optionsColumn castsShadowcheckBox;

Notice that there is a flag here "-p" that parents this checkBox to the optionsColumn. This column was
empty earlier. Now it's time to fill it. We'll fill it the way we filled a window with 50 buttons in the loop example.

If you look at the sketch again you see that I also want a button here. So right after the loop I add these lines:
( I decided to put a separator there too...)

separator -h 46 -w 115;
button -w 116 -l "Edit RenderStats";

Now we already have a UI where the button on the left can update our list and we also have a lot of checkBoxes
here :) Pluss a separator and a button ( A button that doesn't do anything yet... )

Let's see if the checkBox loop works. This is how it looks so far:

I think it's looking very good. This is exactly what we wanted it to look like. I have also selected a few primitives
to check what that looks like.

The remaining stuff to add here are two procs. The first one is a bit advanced, but the other one is rather easy.
This is the first one:

global proc ma_renderStatCommand (int $ma_whichCommand)
{
global string $ma_meshArray[];
string $ma_listShapes[] = `textScrollList -q -si ma_multiAttrEdTSL`;
for ($ma_shapes in $ma_listShapes)
{
setAttr ($ma_shapes + "." + $ma_meshArray[$ma_whichCommand]) (`checkBox -q -v ($ma_meshArray[$ma_whichCommand] + "checkBox")`);
}
}

Here we actually have a proc with an argument for the first time during the scripting of this script. Time to put
on the "focus-hat" ladies and gentlemen...

  • ma_renderStatCommand (int $ma_whichCommand): Just the name of the proc and we also tell Maya
    that we wish to use an integer to do something inside the proc.
  • global string $ma_meshArray[]; But this is the variable we declared earlier in the script... Yes it is, but
    we have to tell Maya that we intend to use it her too. Since we are inside the proc, Maya pays no
    attention to what's on the outside. We just went inside a proc-house. Now we say "Maya! Remember that
    I declared a value outside the proc?... Well I intend to use it again so I'll just redeclare it in here OK?".
    We're allowed to use it again as long as we redeclare it. However you don't have to cast values to it.
    Maya will remember the values.
  • Then we query the selected objects in the textScrollList ( -q -si (selectItem) ) and we cast the list to the
    array $ma_listShapes.
  • We use a for in loop to go through all the selected objects and setting the new renderStats for them.
    Now, let's take a closer look at the command in that loop....
    --> setAttr ( command for "set attribute" )
    --> ($ma_shapes + "." + $ma_meshArray[$ma_whichCommand]) ( object + attribute ) Here we include
    the integer we wanted as an argument. This means that when I call this proc I have to write:
    ma_renderStatCommand 0 to set the value for castsShadows and so on.
  • (`checkBox -q -v ($ma_meshArray[$ma_whichCommand] + "checkBox")`): But since setAttr needs two
    arguments like this: "setAttr object.attribute value" we need to add this. This line is a bit tricky. We
    directly query the state of the checkBox and then this becomes the value we set (say we call the proc
    again with the argument 0: checkBox -q -v castsShadowcheckBox ) If it equals 1 the attribute will be turned on, if it equals 0 the attibute will be turned off.

But what are we going to use this proc for? We cant call it 9 times to go through it? ( the array have 9 values,
but starts at 0 remember so the last one is 8 / see the next proc ) Are we going to assign that proc to the
button? That won't make sense will it?

We build another proc, that's what we do. And inside that proc we'll loop through all the checkBoxes and set the
attributes according to the queried values. Here it is:

//Check the checkBox-values and edit the renderStats:
global proc ma_cycleValues()
{
for ($i = 0; $i <= 8; $i++)
{
ma_renderStatCommand $i;
}
}

 

This proc does all the job for us. It inserts the integer we need as an argument for the previous proc and
loops through it by setting 0, 1,2 and so on... It will loop through all the checkBoxes and set the attributes USING the other proc... Pretty clever huh? Now this is the proc we'd want to add to our button. Now you
already know how to do that so I won't write down the code for that here. Of course you can download the
code below here and you can also download my final version of this script on the resources- page. This is how my version looked when I was finished with it:

You should be able to make something similar if you browse the helpFiles a bit ;)

NOTE: The script we wrote together is downloadable here, but it's not cleaned. I suggest you download the one
in the resource section and compare them. Because the script I have there is an official release and therefore
the script is commented, icons included and also a readMe with install-notes are supplied.

There are several things you could do to improve the script, but I think this will do for now. Of course you can
do some research and try rewriting it to do other tasks too :)

In this tutorial I have gone through the very essens of MEL scripting. I have only scratched the surface, but I
feel the things I went through her is essensial if you want to write short code that can do big things.
--> commands
--> varialbes
--> strings, ints, arrays, floats
--> if statements
--> loops
--> procedures.

And as I have said before: Even if it can seem a bit unclear you'll grow with experience and learn while
doing. I'm just trying to push you in the right direction here. If you have any comments or questions you can
reply in this cgTalk thread or use the contact form on my site to get in touch with me directly.

to download, rightClick and "save target as..."

Thank you for sticking with me throughout all these pages... Enjoyed writing it.

 
 
 
 

The content of this tutorial is copyrighted by law.
Reproduction of the content without permission
will result in legal prosecution.

www.final3.com © 2006