User Tools

Site Tools


en:software:matlab:trepr:dev:gui:design

GUI Design

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.

Writing GUIs programmatically

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.

Units

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.

The parts of a GUI function file

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:

  • Construct the controls
  • Initialise the GUI
  • Define the callback routines
  • Define additional internal functions (not callback routines)

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:

  • Make the main GUI window invisible at the beginning.
  • Add callbacks for keypress events and window closing.
  • Add handles of the GUI to guidata.

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)

1)
It is always a good idea to tidy up before you leave…
2)
If you make use of those lines, make sure to comment them afterwards, to get rid of the weird lines on the Matlab™ command line.
en/software/matlab/trepr/dev/gui/design.txt · Last modified: 2020/09/30 21:35 by 127.0.0.1