The MATLAB programming environment is flexible and there are many ways to achieve the same functionality, especially in GUI programs. I would like to illustrate one possible architecture that I have used successfully on several projects and which takes advantage of several new features in MATLAB 5. As an example, we will develop a simple graphical application, demogui. This application will display a window with an editable text field containing a number (initially "1") and two buttons which will allow us to increment or decrement this number by 1. The design of the user interface and the functionality of the application are often the most difficult aspects of GUI programming; the programming itself is not that hard.
All functionality is programmed into a single m-file with a single argument which serves as a function selector. The various operations are to be implemented by calling this m-file with different values for the selector. The initialization of the application is achieved by calling demogui with no arguments.
Design the layout of the application's figure. It is important to give this some thought and you may want to consult the users of your application. For demogui, the figure will look like this:
Design the functionality to be implemented. Our application has five functions which need to be handled:
These five functions are to be handled by calling demogui with one of five selectors, 0 through 4, respectively. So, initially, demogui looks like this (note that we are using the switch statement which is a new feature of MATLAB 5):
function demogui(selector) switch selector case 0 % Initialize GUI application case 1 % Decrement counter case 2 % Increment counter case 3 % Enter value in edit box case 4 % Close requested end
Now we just have to fill in the code for the various cases.
In this step the graphical elements and data structures are created. There are four objects: the figure, two buttons and an edit box. For each object we must specify its position and other properties, especially callback functions. The callback of a uicontrol is the code that is executed when the control is actuated. We define these functions to be demogui itself, but with a non-zero selector code. Other properties specify the style and appearance of the object. The following statements will create the figure and the controls.
figure('Position',[245 380 150 90],... 'CloseRequestFcn','demogui(4)',... 'Resize','off'); uicontrol('Style','pushbutton',... 'Position',[20 20 50 20],... 'String','Down',... 'CallBack','demogui(1)') uicontrol('Style','pushbutton',... 'Position',[80 20 50 20],... 'String','Up',... 'CallBack','demogui(2)') uicontrol('Style','edit',... 'Position',[45 50 60 20],... 'String','1',... 'CallBack','demogui(3)');
We are still missing something, however. It is necessary to save the handle to a control if it needs to be referenced later on. Usually, these references will take place in a callback function so we need a place to save these handles. It can be done with global variables, but a more elegant technique takes advantage of another new feature in MATLAB 5, namely, structures. It is possible to save any data you want in a structure and then save that structure in the UserData property of the figure. For this example we will need to save the handle to the edit box and we will also save a count variable that keeps track of the value in the box. So, demogui now looks like this:
function demogui(selector) switch selector case 0 % Initialize GUI application fig = figure('Position',[245 380 150 90],... 'CloseRequestFcn','demogui(4)',... 'Resize','off'); info.count = 1; uicontrol('Style','pushbutton',... 'Position',[20 20 50 20],... 'String','Down',... 'CallBack','demogui(1)') uicontrol('Style','pushbutton',... 'Position',[80 20 50 20],... 'String','Up',... 'CallBack','demogui(2)') info.editbox = uicontrol('Style','edit',... 'Position',[45 50 60 20],... 'String','1',... 'CallBack','demogui(3)'); set(fig,'UserData',info,... 'HandleVisibility','callback') case 1 % Decrement counter case 2 % Increment counter case 3 % Enter value in edit box case 4 % Close requested end
Setting the figure's HandleVisibility property to 'callback' will insulate your application from the command line. It causes gcf to return the handle to your figure, but only when executed in a callback function (more on this later) and any plotting will open a new figure rather than drawing in your figure.
Now we implement the required functionality of the callbacks. We have defined a pushbutton control labeled 'Up' so that, when pressed, the function demogui(2) is executed in the current workspace. The variables defined during the initialization step no longer exist so we must retrieve what we need from the figure's UserData. The code
fig = gcf; info = get(fig,'UserData'); info.count = info.count + 1; set(info.editbox,'String',num2str(info.count)) set(fig,'UserData',info)
will do what we want.
In order to press the 'Up' button our figure must have been the current figure and so the gcf function can be used to get the handle to it. Once we have that it is easy to retrieve any data we saved in the UserData. Structure elements can be used just like any other variables so we increment the count and then put a string version of the new value into the String property of the edit box (whose handle we also saved). Finally, we save the modified info structure back into the figure's UserData. That's all there is to it. The code for the 'Down' button and typing arbitrary numbers in the edit box follow the same pattern.
The final callback handles a close request. This occurs when the user clicks on the figure's close box. In MATLAB 4 this would close the figure unconditionally, destroying the application and losing any data saved in the UserData. MATLAB 5 figures have a property called CloseRequestFcn. By default, this function will just close the figure and the effect is as in version 4, but it is possible to redefine this callback function so that the close request is trapped. In particular, we can implement a dialog box which asks if that is really desired.
One more detail: we stated in the beginning that we wanted to run our application by calling demogui with no arguments. For that to work we must check for the number of arguments and, if zero, set the selector to 0. The final version of demogui appears below.
It is worth noting that this programming style allows you to run multiple instances of your application. In particular, we can type demogui twice at the command line and we will get two figures, each with its own environment. Button presses in one figure will have no effect on the other.
function demogui(selector) if nargin == 0 selector = 0; end switch selector case 0 % Initialize GUI application fig = figure('Position',[245 380 150 90],... 'CloseRequestFcn','demogui(4)',... 'Resize','off'); info.count = 1; uicontrol('Style','pushbutton',... 'Position',[20 20 50 20],... 'String','Down',... 'CallBack','demogui(1)') uicontrol('Style','pushbutton',... 'Position',[80 20 50 20],... 'String','Up',... 'CallBack','demogui(2)') info.editbox = uicontrol('Style','edit',... 'Position',[45 50 60 20],... 'String',num2str(info.count),... 'CallBack','demogui(3)'); set(fig,'UserData',info,... 'HandleVisibility','callback') case 1 % Decrement counter fig = gcf; info = get(fig,'UserData'); info.count = info.count - 1; set(info.editbox,'String',num2str(info.count)) set(fig,'UserData',info) case 2 % Increment counter fig = gcf; info = get(fig,'UserData'); info.count = info.count + 1; set(info.editbox,'String',num2str(info.count)) set(fig,'UserData',info) case 3 % Enter value in edit box fig = gcf; info = get(fig,'UserData'); newcount = sscanf(get(info.editbox,'String'),'%d'); if ~isempty(newcount) info.count = newcount; end set(info.editbox,'String',num2str(info.count)) set(fig,'UserData',info) case 4 % Close requested answer = questdlg('Really close demogui?','','Yes','No','No'); if strcmp(answer,'Yes') delete(gcf) end end
Doug Schwarz' home page.