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. |