This page is not about how to design proper GUIs in terms of the layout of their control elements, but rather about how to transfer the design into Matlab™ code.
The term “programmatically” stems from the Matlab™ documentation and refers to the fact that all the control elements of the GUI get explicitly coded in a Matlab™ function file (“m-file”). The alternative would be to use Matlab™'s GUIDE tool, a GUI to design GUIs. This would generate a Matlab™ figure file (“fig-file”) containing the actual GUI controls in a nonaccessible way, and an accompagnying m-file with the callbacks and stuff.
To have full control over all the elements, I chose the first option, “programmatically” writing the GUI, and so far it proved to be very useful.
The Matlab™ help will tell you to use relative units wherever possible. While that might be useful in terms of being able to resize the GUI, resizing is something that I don't take into account for the toolbox GUIs. They are designed to fit to a rather small laptop screen, and if used at a bigger screen, you can arrange the different subwindows next to each other rather than on top of each other, as it is the standard behaviour (steming from the fact that the toolbox GUI gets designed mainly on a 13“ MacBook).
To have maximum control over the positioning, use “Pixels” as units for all uicontrol
elements.
The default font size (the “FontSize” property of an uicontrol
) should be 12.
Each GUI function file consists of four different parts that for the sake of consistency should follow each other always in the same sequence. Namely, these are:
Besides that, every GUI function file should of course start with the usual comment header that Matlab™ will print if you call help
or doc
with the respective function name, followed by a two-liner with the copyright notice and date of the last change.
What follows is the skeleton of a generic GUI file that gives an impression of the different parts and can serve as template for your own purposes.
function mySingletonGUI % MYSINGLETONGUI A GUI window stub demonstrating the singleton concept % in Matlab(tm). % (c) 2012, Till Biskup % 2012-05-31 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Construct the components %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Make GUI effectively a singleton singleton = findobj('Tag',mfilename); if (singleton) figure(singleton); return; end % Construct the components hMainFigure = figure('Tag',mfilename,... 'Visible','off',... 'Name','My singleton GUI',... 'Units','Pixels',... 'Position',[300,300,350,250],... 'Resize','off',... 'NumberTitle','off', ... 'Menu','none','Toolbar','none',... 'KeyPressFcn',@keypress_Callback,... 'CloseRequestFcn',@closeGUI... ); uicontrol('Tag','some_edit',... 'Style','edit',... 'FontUnit','Pixel','Fontsize',12,... 'HorizontalAlignment','Left',... 'Visible','on',... 'Units','pixels',... 'String','',... 'Position',[10 10 80 25],... 'Callback',{@edit_Callback,'something'}... ); % Add some more elements here %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Initialisation tasks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Store handles in guidata guidata(hMainFigure,guihandles); % Make the GUI visible. set(hMainFigure,'Visible','on'); % Add keypress function to every element that can have one... handles = findall(... allchild(hMainFigure),'style','pushbutton',... '-or','style','togglebutton',... '-or','style','edit',... '-or','style','listbox',... '-or','style','popupmenu'); for m=1:length(handles) set(handles(m),'KeyPressFcn',@keypress_Callback); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function closeGUI(~,~) try delete(hMainFigure); catch exception try msgStr = ['An exception occurred in ' ... exception.stack(1).name '.']; trEPRmsg(msgStr,'error'); catch exception2 exception = addCause(exception2, exception); disp(msgStr); end try trEPRgui_bugreportwindow(exception); catch exception3 % If even displaying the bug report window fails... exception = addCause(exception3, exception); throw(exception); end end end function keypress_Callback(~,evt) try if ~isempty(evt.Modifier) if (strcmpi(evt.Modifier{1},'command')) || ... (strcmpi(evt.Modifier{1},'control')) switch evt.Key case 'w' closeGUI(); return; otherwise return; end end end switch evt.Key case 'f1' return; otherwise % disp(evt); % fprintf(' Caller: %i\n\n',src); return; end catch exception try msgStr = ['An exception occurred in ' ... exception.stack(1).name '.']; trEPRmsg(msgStr,'error'); catch exception2 exception = addCause(exception2, exception); disp(msgStr); end try trEPRgui_bugreportwindow(exception); catch exception3 % If even displaying the bug report window fails... exception = addCause(exception3, exception); throw(exception); end end end function edit_Callback(source,~,action) try % If action is empty, return if isempty(get(source,'String')) return; end switch action case 'something' % Do something useful here otherwise % fallback disp(['function_name(): edit_Callback(): Unknown field: ' action]); return; end catch exception try msgStr = ['An exception occurred in ' ... exception.stack(1).name '.']; trEPRmsg(msgStr,'error'); catch exception2 exception = addCause(exception2, exception); disp(msgStr); end try trEPRgui_bugreportwindow(exception); catch exception3 % If even displaying the bug report window fails... exception = addCause(exception3, exception); throw(exception); end end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Utility functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Add additional functions here, if necessary. end
A few comments to the above code listing. Parts of it you may already have seen or will see at other pages, such as the singleton concept or the generalised callback routine (in this example for the edit field).
A few things have been added here to make it a more useful example that is actually quite close to a real-world example and therefore can serve as proper template for own developments:
The idea behind defining the main GUI window as invisible (set the Visible
property of the figure
to off
) is to have it first initialise all controls and afterwards make all visible at once. That is especially useful for larger GUIs with loads of elements. Therefore, the last task after initialising the GUI (in the section “Initialisation tasks”) is to make the GUI visible with the statement
set(hMainFigure,'Visible','on');
Another important aspect of the initialisation tasks of the GUI is to store the GUI handles in guidata
:
guidata(hMainFigure,guihandles);
That allows you in all functions within that GUI function to get the GUI handles with a code line like the following:
gh = guihandles(hMainFigure);
That gives you straight access to all handles of all the controls your GUI consists of. And that is the power of giving your GUI controls reasonable names (alias “tags”). In addition, make sure not to give two different controls the same name, as this may confuse your code and lead to problems.
Whereas the generic callback routine has been described elsewhere already, the two additional callback functions deserve some attention.
As you can see from the definition of the main figure window, it gets two specific callbacks added, namely KeyPressFcn
and CloseRequestFcn
.
hMainFigure = figure('Tag',mfilename,... ... 'KeyPressFcn',@keypress_Callback,... 'CloseRequestFcn',@closeGUI... );
The first gets called whenever you press any key on your keyboard while this figure window has the focus. The latter gets called when you try to close the GUI window, e.g. by pressing the “x” in the top left or right corner of the window (or if you, knowing about the window handle, try to call close()
with the window handle as argument).
Whereas in our example, the closeGUI()
callback function is pretty boring, besides the try
-catch
statement it consists only of a single statement, namely delete(hMainFigure)
, it might well contain additional functionality. Suppose you want to check whether there are still some unsaved data in your GUI, or whether still some subwindows are open, that you should close before closing the GUI window1).
The other function, keyBindings()
, handling the keyboard inputs, is much more interesting. This is one of the still rare occasions where you actually need the second generic input argument of Matlab™ callback functions, the event
argument, as it contains in this case the keyboard codes and therefore the information about the key(s) pressed.
To add the keypress_Callback()
callback routine to all appropriate controls of your GUI and not only to the main window, there is an additional code block included in the “initialisation tasks”:
% Add keypress function to every element that can have one... handles = findall(... allchild(hMainFigure),'style','pushbutton',... '-or','style','togglebutton',... '-or','style','edit',... '-or','style','listbox',... '-or','style','popupmenu'); for m=1:length(handles) set(handles(m),'KeyPressFcn',@keypress_Callback); end
The keypress_Callback()
callback routine itself gives you an example how to deal with modifier keys, as it adds the standard keyboard shortcut <key>Ctrl</key>+<key>w</key> for closing the GUI window. The rest of the callback function serves merely as template without function. But you can see how to add, for example, a shortcut for a function key. And the commented part shows you an easy way to print the pressed key to the Matlab™ command window for debug purposes.2)