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