Adverti horiz upsell
Conversational MEL Part 2
Conversational MEL Part 2
JamesPiechota, updated 2006-11-29 06:52:29 UTC 35,030 views  Rating:
(5 ratings)
Page 4 of 4

for-in Loop


We�ve covered a lot of MEL in this article and the one before - but there�s still a lot to go. Two important concepts are �loops� and �conditionals� - together this dynamic duo is known by its alter ego �flow and control�. We�ll be covering this in detail in an upcoming article. For now, though, we�ll touch briefly on the simplest type of loop: the for-in loop.

Let�s say you wanted to print out the names of all the objects in your scene. You could try:

string $objects[];
$objects = `ls`;
print $objects;

Which would output something like:

time1
renderPartition
renderGlobalsList1
defaultLightList1
defaultShaderList1
.
.
.

But let�s say you�re not really feeling that whole �vertical� thing - you�d rather have all of the names on the same line separated by commas. You could try:

string $objects[];
$objects = `ls`;
print $objects[0];
print �, �;
print $objects[1];
print �, �;
print $objects[2];
print �, �;
print $objects[3];
print �, �;
.
.
.

This would work - but it probably wouldn�t take long before you decided that vertical isn�t that bad after all.

Enter the �for-in� loop.

The for-in loop will walk through every element in an array and, one by one, assign the value of the element to a temporary variable that you can muck around with. Here�s our �print� example written with a for-in loop:

string $objects[];
$objects = `ls`;string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
print ", ";
}

If you give the script a quick scan you may notice the for and in words from which we get the name �for-in loop� - pretty clever, eh?

string $objects[];
$objects = `ls`;

string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
print ", ";
}

These lines you�ve seen before. They declare a string array and assign it the return value of the �ls� command.

string $objects[];
$objects = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
print ", ";
}

Here we declare the string variable that will be used in the for-in loop. This variable will get assigned each element in the $objects array one after the other.

string $objects[];
$objects = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
print ", ";
}

This is the loop itself. As mentioned before: the for-in loop will walk through every element in an array and, one by one, assign the value of the element to a temporary variable that you can muck around with. In this chunk of code:

$currentObject
is the temporary variable

$objects
is the array we are walking through

print $currentObject;
print ", ";
is the mucking about we referred to - more formally known as the loop body.

The '{}' are called curly braces and their only purpose is to delimit the loop body. They'll pop up again in future articles - for now just remember to put a '{' before the loop body and a '}' after the loop body.

The for-in loop causes the loop body to be executed several times in a row, each time with a different value in the temporary variable. Executing the loop is the same as executing:

$currentObject = $objects[0];
print $currentObject;
print �, �;
$currentObject = $objects[1];
print $currentObject;
print �, �;
$currentObject = $objects[2];
print $currentObject;
print �, �;
$currentObject = $objects[3];
print $currentObject;
print �, �;
.
.
.

Don�t worry if you�re not too comfortable with the for-in loop at this point. This was just a quick and dirty overview to get you up and running with the search-and-replace script. We�ll get into it nice and proper in a later article.

Designing Search-And-Replace


Here we are, over 4,000 words later and we haven�t even started to tackle the search-and-replace script promised way back at the beginning. Believe it or not, the hard part is over. With everything we�ve gone over here, plus two new commands, the script will practically write itself.

From the intro: the search-and-replace script will examine every object in the scene looking for a word you specify. When found it will rename the object by replacing the specified word with something else.

As the scripts you write get more complex, it may be a good idea to spend some time upfront thinking about how to structure the code. One way to do this is to take a plain-english description of the problem to solve and break it down into steps that match MEL language a little more closely. Doing this with the above description we might end up with:
  • �examine every object in the scene�
  • �look for a specified word�
  • �rename the object by replacing the specified word with something else�

This is still a far cry from actual MEL - but at least we�ve identified some key tasks. The next step might be to start thinking about how you might use MEL to accomplish each of these tasks. Again don�t worry about writing any real MEL code at this point.
  • �examine every object in the scene�
    • Use the �ls� command and a for-in loop
  • �look for a specified word�
    • Uh oh
  • �rename the object by replacing the specified word with something else�
    • Uh oh

Oops - turns out we got a couple problems. Luckily there are two handy MEL commands to help us out: substitute and rename.

The substitute command will replace a portion of one string with another. For example:

substitute "lias" "Alias" "utodesk";

substitue command arguments


... will search through the target string �Alias� looking for the search string �lias�, if it finds it will replace it with the replace string �utodesk�. The final, substituted string, is passed back through the return value.
If the string isn�t found, like in this attempt to turn �Howdy!� into �Y�all come back now, y�hear.� ...

substitute �Hello!� �Howdy!� �Y�all come back now, y�hear.�;
// Result: Howdy! //

... then the original string is returned unchanged.

The rename command is used to rename nodes. It takes two command arguments: the name of the object to rename, and the name to rename it to. For example:

rename �pSphere1� �Bob�;

Before renaming...


... after renaming


Okay, now we can fill in those missing bits:
  • �examine every object in the scene�
    • Use the �ls� command and a for-in loop
  • �look for a specified word�
    • Use the �substitute� command
  • �rename the object by replacing the specified word with something else�
    • Use the �rename� command

As we can see the red notes don�t line up exactly with the descriptive lines - that�s okay. Normally you would go through this exercise just to get an idea for the structure of your script and the tools you�ll have to use.


Coding Search-And-Replace


Let�s write some code!

Looking at our notes we see that the first item is �Use the �ls� command and a for-in loop�. Let�s jot that down to give us a framework:

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
}

You�ll notice that the loop body is empty and in fact executing this script does nothing at all.

No output


If this weirds you out, you can fill in a temporary body:

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
}

Adding the print command will also help us verify that everything we�ve written so far is working.

Next we have �Use the �substitute� command�. Okay this one�s a little tougher because we need to have three strings to pass it as command arguments. Let�s write the command anyways and fill it in as we go:

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
substitute;
}

Since we�re trying to replace a portion of the each object�s name, $currentObject is a good candidate for one of the substitute command arguments:

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
substitute $currentObject;
}

That leaves only the string that we�re searching for and the one that we�re replacing it with. These strings are what is knows as user input because they need to be provided by the user - as opposed to information like $currentObject which we can grab from the scene automatically without any user involvement.

Lacking a real user you may have to play one yourself: once you come up with a search string and a replace string, slap them down into the substitute command (don�t forget its slightly awkward argument ordering):

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
substitute �pCube� $currentObject �Bob�;
}

I�ve chosen to replace all occurrences of �pCube� with the string �Bob�.

It�s usually a good idea to execute your code periodically to try and catch any syntax errors earlier rather than later.

No errors


Good, no errors.

Our final note is �Use the �rename� command�. Well that�s pretty easy, all we have to do is rename the $currentObject to the substituted string:

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
substitute �pCube� $currentObject �Bob�;
rename $currentObject Uh Oh!!
}

What do we rename it to? Our outline got us pretty far, but usually there�ll be some blanks to fill in. From before we learned that the �substitute� command passes back the substituted string as its return value. So all we need to do is store that value and then use it in the �rename� command:

string $objects[] = `ls`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
string $substitutedString = `substitute �pCube� $currentObject �Bob�`;
rename $currentObject $substitutedString;

}

Holy moly! Is that it??! Are we done? Let�s create some objects ...

Before the first run


... and give it a try:

Error: Can't rename a read only node


Oik - what does that mean?? Luckily because of our �print� command:

print $currentObject;

... we know that we were trying to rename the �time1� object when the error occurred. Even if you don�t know what an error message means, you may be able to make a judgment call about whether it even needs fixing. I don�t know about you, but I don�t give a flying hoo-ha about the �time1� node. In fact I saw that huge list of object names returned by the �ls� command and I honestly have no interest in renaming most of them. Rather than fix this error let�s see if we can�t skip over it.

We opened this article with a discussion of the �ls� command - one the most often used commands out there. Because of its well-worn position in the scripter�s toolbox, the �ls� command has evolved several command flags over the years (41 as of Maya 7.0). Each of these 41 flags tweaks the �ls� command a little bit, changes what types of objects are listed, what sort of information is included about each object, etc...

There are so many flags, though, that you may be hard-pressed to remember them all. In a later article we�ll discuss how to read the MEL Command Reference and get all the juicy details of all of Maya�s MEL commands. For now, I�m just gonna wave my magic command flag wand and hope one appears:

-selection

The �-selection� flag causes the �ls� command to only list selected objects (its short form is �-sl� - how�s that for a palindrome, madam!). Not only will this allow us to avoid that error message - but heck we could probably even spin it as a feature.

Once we slip that new flag into place we end up with our script looking like:

string $objects[] = `ls -sl`;
string $currentObject;
for ( $currentObject in $objects )
{
print $currentObject;
string $substitutedString = `substitute �pCube� $currentObject �Bob�`;
rename $currentObject $substitutedString;
}

Remembering to select something first, let�s give this one final run:

Success!


Rock on!

Conclusion


In this article we covered a lot of hardcore programming concepts. We discussed variables and arrays, data types and loops, assignment and return values... And in the end we tried to tie it all together by writing a complex, and hopefully useful, script from scratch. (Did you know that there are currently 22 scripts on Highend 3D that deal with renaming nodes? Sure many of them have UI and other bells and whistles - but the script you just completed isn�t that far off!)

Don�t worry if you�re still a little unsure on some of concepts introduced here. Variables, arrays, and loops are the building blocks of all scripts and as such will come up again and again as we delve deeper into MEL and explore more of the exciting Maya features it lays bare.

As always if you have any comments or questions please feel free to email us at: info@NimbleStudiosInc.com