Popular Posts

Tuesday, February 22, 2011

Mel Script Example: Keyword Search

For my first post, I though I'd show you how to do something I though was cool, a keyword search feature.






1. Create an array of objects to be stored in a list.

You need to store a list of object names into an array. For this example, we will create an array with names.


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};



2. Create a window.


Now we need to create a window in Maya that will show you the list of names. Add this part underneather the string array you just made.


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};


window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;


showWindow;

When scripting, you will often rerun your script multiple times to make sure that it is working properly. Your going to want to check to see if this window already exists and if it does, you want to delete it so the new window can replace it. you can do this by adding


if ( `window -exists nameWindow` ) {
      deleteUI nameWindow;
}


this part of the code says;

If a window named "nameWindow" exists...
          ... delete the window named "nameWindow".




You should add this code right above the window line if the script, like this;


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};

if ( `window -exists nameWindow` ) {
      deleteUI nameWindow;
}



window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;

showWindow;



3. textScrollList

What is a textScrollList? Click Here.


Now we need to add a textScrollList. This is GUI element that will show you everything in your list. Your going to need to update the your window script.


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};



if ( `window -exists nameWindow` ) {
 deleteUI nameWindow;
}



window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;


             //textScrollList is the GUI element that the list will be stored into.
             textScrollList -numberOfRows 8
                                 -allowMultiSelection false
                                 nameList; // The name of this element is "nameList".


showWindow;



When you execute your script (Ctrl + Enter), you should see this. If you notice, there is nothing inside the window. This is because you didn't actually add anything to the list yet. 

4. Adding items to the List.

Now that we have a window to view our list, we need to add objects to the textScrollList. We are going to create a "for loop" after the textScrollList section of the script that simply states;

for each name in the list of names...
          .... add the name to the textScrollList named "nameList"


In Mel, this would look like this:

for ($eachName in $list_of_names){
          textScrollList -edit -append $eachName nameList;
}


So here is what the whole code should look like now.


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};


if ( `window -exists nameWindow` ) {
     deleteUI nameWindow;
}



window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;

   //textScrollList is the GUI element that the list will be stored into.
   textScrollList -numberOfRows 8
                      -allowMultiSelection false
                           nameList; // The name of this element is "nameList".

                    for ($eachName in $list_of_names){
                             textScrollList -edit -append $eachName nameList;
                    }

showWindow;



You should see something like this when you execute the script.


5. textField

We are going to add a textField under the textScrollList so you can filter the items in the list based on whatever you type in this field.

text "Keyword Search";
textField -text "" keyword;


I added a some text right above this field just to label the search field. 
The textField has a flag called "-text" followed by 2 quotations signs. Whatever is between those 2 quotation signs will be the default text that will appear in this field when the window is created. Right now we dont want anything there so there is nothing inside of those quotations. After the quotes, the name of the textField is placed.

Here is what the script should currently look like;

//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};

if ( `window -exists nameWindow` ) {
 deleteUI nameWindow;
}

window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;

   //textScrollList is the GUI element that the list will be stored into.
   textScrollList -numberOfRows 8
                      -allowMultiSelection false
                           nameList; // The name of this element is "nameList".

                    //create a for loop stating for each name in the list of names...
                    for ($eachName in $list_of_names){
                             textScrollList -edit -append $eachName nameList;
                    }

      text "Keyword Search";
   textField -text "" keyword;
showWindow;



Here is what you should see when you execute the script right now. 

6. Creating the filter procedure.

If you type anything into the textField below the words "Keyword Search", nothing will happen right now. This is because we need to create a procedure to that runs when you enter text into this field. We want this procedure to run every time you enter some text into the textField and press enter.

The procedure goes above everything we have done so far.

proc keyword_filter(){
}


We named the procedure "keyword_filter". You will notice closed parentheses after the name. This is where we will input variables into the procedure. Eventually we want to input the list of names into this procedure, but for now we will create a temporary array. We will name this array "temp_array";

proc keyword_filter(string $temp_array[]){
}

Now get the value of the textField we created for our keyword search. We already named this textField "keyword", now we have to find the value in this field. We do this by querying the textField and storing it into a string variable


proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
}

We want to also create another array inside the procedure. This procedure is going to be used to store the filtered name list. Since we dont know what anyone is going to type into the search filter, we dont know how big this array has to be. So we are going to create an int variable with a value of 0. 

proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

}

The filter needs to be run on every name in the list to see if it matches the keyword. So we are going to make a for loop to go through every variable in the temp_array.

proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

     for ($each in $temp_array){
     }

}


Inside the for loop, we need to write what happens to each variable being looked at. We are going to run a match command (Click Here for more info on the match command). The match command will compare the each name with whatever keyword has been entered. If it finds a matching letter combination, it will return a string of the matching letters. if it doesn't find one, then it will return a string with no characters at all. This how you run a match command;

string $findMatch = `match $keyword $each`;

here is where it would be placed in the procedure..

proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

     for ($each in $temp_array){
               string $findMatch = `match $keyword $each`;
     }
}

Now we to have the procedure actually check to see if the 2 variables match. We are going to insert an "if" statement here. Pretty much saying:

if the letters in the keyword matches letters in the current name...
          ... add it to the "filteredList".

this is written like this:

if ($findMatch == $keyword){
     $filteredList[$i] = $each;
}

We also have to make sure that the information is properly being stored. This is where the int variable if $i comes into play. Right now it equals zero, so when you add the name to the filtered list, you are going to want to put it the position equal to the value of $i. 

$filteredList[$i] = current name;

so if $i is equal to 0, then;

$filteredList[0] = current name;

Remember that a for loop will repeat this for every name in the list. So every time tries to put a new name in the "filteredList", it will try to put it into the zero position. To prevent this, just add one to the value of $i.

$filteredList[$i] = current name;
$i++;

by adding "++" after an int variable, it will add one to it's value.


//$i = 0;
$filteredList[$i] = current name;
$i++;

//$i = 1;
$filteredList[$i] = current name;
$i++;

//$i = 2;
$filteredList[$i] = current name;
$i++;

//$i = 3;




So here is what the for loop should look like added to the procedure:

proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

     for ($each in $temp_array){
               string $findMatch = `match $keyword $each`;
               if ($findMatch == $keyword){
                       $filteredList[$i] = $each;
                       $i++;
               }

     }
}

    
Finally, the last part of procedure is to update the list in the window with the filtered list. We are going to be removing the items from the textScrollList first;

     textScrollList -edit -ra nameList;

Then we are going to add each name in the filtered list to the textScrollList. We are going to do this the same way we originally added items to this list.

     for ($each in $filteredList){
              textScrollList -edit -append $each nameList;
     }

Here is what you should have for the procedure added to the script.


proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

     for ($each in $temp_array){
               string $findMatch = `match $keyword $each`;
               if ($findMatch == $keyword){
                       $filteredList[$i] = $each;
                       $i++;
               }

     }
     textScrollList -edit -ra nameList;
     for ($each in $filteredList){
              textScrollList -edit -append $each nameList;
     }

}


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};

if ( `window -exists nameWindow` ) {
 deleteUI nameWindow;
}

window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;

    //textScrollList is the GUI element that the list will be stored into.
    textScrollList -numberOfRows 8
                        -allowMultiSelection false
                           nameList; // The name of this element is "nameList".

                    //create a for loop stating for each name in the list of names...
                    for ($eachName in $list_of_names){
                             textScrollList -edit -append $eachName nameList;
                    }

      text "Keyword Search";
    textField -text "" keyword;
showWindow;


7. Adding a command to run the procedure.

....and the search still doesn't work. This is because when we created the textField, we didn't have the filter procedure written yet. Now that it is done, find the textField line in your script and add a command to it.

textField -text ""-cc "keyword_filter($list_of_names)keyword;

the "-cc" part means "Change Command". After you enter text into this field it will run the command that follows.  Take the name of the procedure, which is "keyword_filter". You still need to add the parentheses, but instead of using the "$temp_array", write the name of the array with the list names, which is "$list_of_names". The $list_of_names will replace the $temp_array.

Rewrite the procedure name in this line as a string (basically in quotations). When the command is executed, Maya will run the procedure named "keyword_filter($list_of_names)".

Here is the whole script, fully functional.

proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

     for ($each in $temp_array){
               string $findMatch = `match $keyword $each`;
               if ($findMatch == $keyword){
                       $filteredList[$i] = $each;
                       $i++;
               }
     }
     textScrollList -edit -ra nameList;
     for ($each in $filteredList){
              textScrollList -edit -append $each nameList;
     }

}


//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};

if ( `window -exists nameWindow` ) {
 deleteUI nameWindow;
}

window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;

    //textScrollList is the GUI element that the list will be stored into.
    textScrollList -numberOfRows 8
                        -allowMultiSelection false
                           nameList; // The name of this element is "nameList".

                    //create a for loop stating for each name in the list of names...
                    for ($eachName in $list_of_names){
                             textScrollList -edit -append $eachName nameList;
                    }

      text "Keyword Search";
    textField -text ""-cc "keyword_filter($list_of_names)keyword;
showWindow;




Try typing the letter "j" and press enter. The list will update to only show the names that begin with "j".


8. Capital "J" and lower case "j" are NOT THE SAME CHARACTER!!!!

YAY!!!! The script works... but something is off. When you enter "j" into the field and press enter, you will see the names "john" and "joe"... but not "John B". This is because Maya sees capital and lower case letters as different characters. 

If you actually want to fix this, then we need to add a few lines to the procedure. 

     for ($each in $temp_array){
               string $findMatch = `match $keyword $each`;
               if ($findMatch == $keyword){
                       $filteredList[$i] = $each;
                       $i++;
               }
     }


Find this section of the script in the procedure. we need to convert $keyword and $each to lower case letters. To do this; 

string $keyword_lower = `tolower $keyword`;
string $each_lower = `tolower $each`;

Now $keyword_lower is $keyword with lower case letters. The same goes for $each_lower and $each.
Now we just need to swap the variables in the match command and the if statement with the new lower case variables.

     for ($each in $temp_array){
               string $keyword_lower = `tolower $keyword`;
               string $each_lower = `tolower $each`;

               string $findMatch = `match $keyword_lower $each_lower `;
               if ($findMatch == $keyword_lower ){
                       $filteredList[$i] = $each;
                       $i++;
               }
     }

This is how the final for loop should look. Now when added to the whole script;


proc keyword_filter(string $temp_array[]){
     string $keyword = `textField -q -text keyword`;
     string $filteredList[];
     int $i = 0;

     for ($each in $temp_array){
               string $keyword_lower = `tolower $keyword`;
               string $each_lower = `tolower $each`;

               string $findMatch = `match $keyword_lower $each_lower `;
               if ($findMatch == $keyword_lower ){
                       $filteredList[$i] = $each;
                       $i++;
               }
     }


     textScrollList -edit -ra nameList;
     for ($each in $filteredList){
              textScrollList -edit -append $each nameList;
     }
}

//An array of people's names.
string $list_of_names[] = {"mike", "john", "joe", "brian", "John B"};

if ( `window -exists nameWindow` ) {
 deleteUI nameWindow;
}

window -title "List of Names" nameWindow;
// The window title is "List of Names" and the window Name is "nameWindow".
       columnLayout -adjustableColumn true;

    //textScrollList is the GUI element that the list will be stored into.
    textScrollList -numberOfRows 8
                        -allowMultiSelection false
                           nameList; // The name of this element is "nameList".

                    //create a for loop stating for each name in the list of names...
                    for ($eachName in $list_of_names){
                             textScrollList -edit -append $eachName nameList;
                    }

      text "Keyword Search";
    textField -text ""-cc "keyword_filter($list_of_names)keyword;
showWindow;

Now type "j" and press enter. All 3 "J" names should appear in the list, regardless of it being capital or lower case letters.










3 comments: