Tutorials


 

MEL Window Shopping

Here's a short tutorial on doing a user interface in MEL.  I mean windows with controls and stuff.  This is not meant to be a comprehensive guide or a bible on doing this stuff.  It's only to get you going.  I'll just cover some (the most used ?) items in a user interface so that you may see how it is generally done.  Another reason for me to write this tutorial is so I can learn this stuff myself...

Ok?  User interfaces come in all shapes and sizes, but there's one thing they all have in common, the

Window

Let's start by making a window.  The cryptic MEL command for making a window is.....window.  Smart.  Try writing the following in the Maya script editor:
 
window -widthHeight 300 200 -title "Jo's window" JoWindow;
showWindow JoWindow;

The first line creates a window which is 300 pixels wide and 200 pixels high.  The title of the window is "Jo's window".  The name of the window is JoWindow.  All UI elements may have names.  If you plan to do something to the element later on, you need a way to say which element you want to use.  That's when you use the name.  For instance in the second line.  I want to show the window named JoWindow.

If you try to run this script, you'll get this:

Pure art, but a bit boring.  There's something missing.  To be able to control a MEL script using this interface, you need....controls.  Controls also comes in many shapes and sizes (just like user interfaces).  Examples of controls are check boxes, radio buttons, buttons, sliders etc.  We'll dig into controls later, but first, we need to handle a small problem.  First, close the window that you just created.  Then, try copying those two lines of MEL code, past them into the script window again and run it one more time.  Crisis!  Error message!

Error: Object's name is not unique:  JoWindow

What the...  It worked before!  Ok, calm down. Here's the catch:  The first time you run the script, you first created the window (the first line) and then you displayed the window (second line).  And then you closed it by pressing the little 'x' button in the upper right corner.  You closed it.  Not destroyed.  Closed.  Somewhere in the murky depths of your computer it is still lurking around waiting for a new chance to show itself to the world again.  And then when you try to run the same script again, you try to create another window named JoWindow.  Now, Maya wouldn't let you have two interface elements with the same name, so you get an error instead.  One way of getting around this is to check if the window exists and then delete it if it does.  The code for this is
 
if (`window -exists JoWindow`)
  deleteUI JoWindow;

Put this in front of the two lines that makes the window, and you are in business.

Now on to the controls.  No, wait!  Not yet.  First we have to look at

Layouts

Before we add controls to our window, we need a way to position and organize them.  There are a number of different layouts for you to use.  They include

columnLayout

The columnLayout arranges its contents in a column.  Try
 
window;
columnLayout -columnAttach "both" 5 -rowSpacing 10 -columnWidth 250;
button;
button;
button;
showWindow;

This produces the following window:

The result is a window (without a name or title) with a column containing three buttons.  The width of the column is 250 pixels.  The space between the rows in the column is 10 pixels.  The columnAttach part is a bit more complicated.  If we remove it, the window will look like this:

Ok, the window is smaller too, but that's because I re-sized it in Maya before copying it.  The main thing is that the size of the buttons changed.  They are now just big enough to accommodate the text printed on them.  And button1, button2 etc. is the default text for buttons.  Leaving the columnAttach out is the same as using columnAttach "left" 0.  This says 'Attach the left side of the controls (or other children) in the column to the left side of the column, 0 pixels in.  Using
 
columnLayout -columnAttach "right" 0 -rowSpacing 10 -columnWidth 250;

wIll produce this:

The buttons are the default width and they are attached to the right side of the column, 0 pixels in.  The "both" 5 part attaches the buttons to both the left and right sides of the columns, 5 pixels in from each side.  That means that the buttons will have to stretch.  The width of the buttons are the width of the column minus the pixel inserts, so they are 250 pixels - 2x5 pixels = 240 pixels.

Another easy layout is

rowLayout

This layout arranges the controls (or other elements) in a row in the window.
 
window;
rowLayout -numberOfColumns 3 -width 250;
button;
button;
button;
showWindow;


 

You may have layouts inside layout, too.  Try this:
 
window;
rowLayout  -numberOfColumns 3 -width 350 MainRow;
columnLayout -columnAttach "right" 0 -parent MainRow Column1;
columnLayout -columnAttach "both" 0 -parent MainRow Column2;
columnLayout -columnAttach "left" 0 -parent MainRow Column3;

setParent Column1;
button;
button;
button;
showWindow;

setParent Column2;
button;
button;
button;
showWindow;

setParent Column3;
button;
button;
button;

showWindow;

First we make a rowLayout with place for three columns in that row.  We call this MainRow.  Then we make three columnLayouts with different columnAttach.  These layouts contain -parent flags.  If they had not been there, Maya would think that we first wanted a rowLayout, and below that, three column layouts.  By using the -parent flag and naming MainRow as the parents of the columnLayouts, Maya understands that we want these layouts to be elements inside the rowLayout.  The three columns are named Column1, Column2 and Column3.

Then we construct the nine buttons.  I used setParent to make them children of the correct layout.  Generally, when we construct a control, it becomes the child of the latest created layout.  Without the setParent lines, all buttons would belong to Column3.  We could also have used a -parent flag in the buttons just as we did in the columns.

There are many more layout types, and they all contain many more flags and stuff.  Read the MEL documentation to learn about them all!

Now we are (finally) ready to look at

Controls

We have already used the button control.  Here is another example of a

button

window;
columnLayout;
button -label "BANG" -command "bangProcedure" BangButton;

proc bangProcedure()
{
  print "BANG\n";
}

showWindow;

This produces a window with a button (named BangButton) with the text "BANG" on it.  When pressed, the word BANG is printed in the script window.  This shows how to get a button to do something.  By specifying the -command flag, you can name a procedure that will be called when the button is pressed.  There is another thing that may be nice to be able to do with a button.  You may want to disable and enable it.  To do this with an already existing button, you need to access the button in edit mode.  Let's say I wanted to set the BangButton in disabled mode.
 
button -edit -enable 0 BangButton;

Let's make two buttons.  One is the old BangButton.  The other is an Enable Bang button.  This button will toggle the enable state of the BangButton.  We also incorporate some other of the things we have looked at.
 
if (`window -exists JoWindow`)
  deleteUI JoWindow;

window -widthHeight 300 200 -title "Jo's window" JoWindow;
rowLayout -numberOfColumns 2;
button -label "Toggle Enable" -command "enableProcedure" EnableButton;
button -label "BANG" -command "bangProcedure" BangButton;

proc enableProcedure()
{
  // Is the button enabled?
  if (`button -query -enable BangButton` == 1)
  {
    // Yes it was, disable it
    button -edit -enable 0 BangButton;
  }
  else
  {
    // It was not, enable it now
    button -edit -enable 1 BangButton;
  }
}

proc bangProcedure()
{
  print "BANG\n";
}

showWindow JoWindow;

Examine the enableProcedure().  It queries the state of the enable flag in the BangButton and enables or disables it according to this value.

This is perhaps not a typical use of a button.  For this, the

checkBox

may be more suitable.
 
if (`window -exists JoWindow`)
  deleteUI JoWindow;

window -widthHeight 120 100 -title "Jo's window" JoWindow;

columnLayout  -columnAttach "both" 5 -columnWidth 100 -rowSpacing 10;

checkBox -label "Toggle Enable" -value 1 -changeCommand "enableProcedure" EnableCheckBox;
button -label "BANG" -command "bangProcedure" BangButton;

proc enableProcedure()
{
  // When we get here, we know that the state of the checkbox has changed.  Let's find out what it is

  int $state = `checkBox -query -value EnableCheckBox`;

  if ($state == 0)
    button -edit -enable 0 BangButton;
  else
    button -edit -enable 1 BangButton;
}

proc bangProcedure()
{
  print "BANG\n";
}

showWindow JoWindow;

The enableProcedure() is called every time the state of the checkBox is changed due to the -changeCommand "enableProcedure" flag.  There are also -offCommand and -onCommand that gets called when the state goes off or on.

radioButton

if (`window -exists JoWindow`)
  deleteUI JoWindow;

window -widthHeight 320 100 -title "Jo's window" JoWindow;

// Let's start with a row layout of two columns.  
// The width of the first is 100, the second 200
rowLayout -nc 2 -width 300 -cw 1 100 -cw 2 200 MainRow;

// First column contains a frameLayout
frameLayout -width 100 -height 100 -label "Enable" RadioFrame;

// Second column contains a columLayout
setParent MainRow;
columnLayout -columnAttach "both" 5 -rowSpacing 10 ButtonColumn;

// The framLayout can only have one immediate child.  
// Because we need several radio buttons in there,
// we need a new layout to hold them all

setParent RadioFrame;
columnLayout RadioColumn;

// Let's make radio buttons in the RadioColumn
setParent RadioColumn;
radioCollection AllRadioButtons;
radioButton -label "BANG 1" -changeCommand "RBProcedure" -select RadioButton1;
radioButton -label "BANG 1" -changeCommand "RBProcedure" RadioButton2;

// Lets make two buttons for ButtonColumn
setParent ButtonColumn;
button -label "BANG 1" -command "bangProcedure1" BangButton1;
button -label "BANG 2" -command "bangProcedure2" -enable 0 BangButton2;

showWindow JoWindow;

proc RBProcedure()
{
  // If we are here, the radio buttons have changed state.
  // We need to find out which button is selected.

  string $selectedButton = `radioCollection -query -select AllRadioButtons`;

  if ($selectedButton == "RadioButton1")
  {
    button -edit -enable 1 BangButton1; // Enable button 1
    button -edit -enable 0 BangButton2; // Disable button 2
  }
  else
  {
    button -edit -enable 0 BangButton1; // Disable button 1
    button -edit -enable 1 BangButton2; // Enable button 2
  }
}

proc bangProcedure1()
{
  print "BANG 1\n";
}

proc bangProcedure2()
{
  print "BANG 2\n";
}

Hey, this script is getting big!

The thing about the radio buttons is that they come in collections.  You have to create a radioCollection and then create radioButtons inside it.  Then you specify a procedure that gets called every time the buttons change.  This procedure may read the select state of the radioCollection and find which button is selected now.

I also used a new layout here, the frameLayout.  All this does is to put a nice frame around the radio buttons.  (Well, it can do more, but that's another story).  The trick with this layout is that it can have only one single immediate child, so we need to make another layout inside it before we add buttons and stuff.

One more thing about windows:

Menus

if (`window -exists JoWindow`)
  deleteUI JoWindow;

window -menuBar true -widthHeight 320 100 -title "Jo's window" JoWindow;

menu -label "File" -tearOff true;
          menuItem -label "Load";
          menuItem -label "Save";
          menuItem -divider true;
          menuItem -label "Close" -command "closeWindow";
menu -label "Help" -helpMenu true;
          menuItem -label "About Application...";

showWindow JoWindow;

proc closeWindow()
{
  deleteUI JoWindow;
}

Menus are easy, really.  First, remember to add the -menuBar true to the window when creating it.  Then create the menu.  The menu is created by calling menu for each menu on the menu bar.  The menu will attach to the current window if no parent is explicitly specified.  The, for each menu, create menuItems.  These take -command flags just like buttons so you can specify the procedures that will be run when that menu item is selected.

That's it, more or less.  There are tons of other stuff, layouts, controls, functionality etc. that is not covered here, but by now you should have a fair grasp on doing window based user interfaces in MEL.  Read the rest in the documentation!