MATLAB Answers

How to create a GUI

116 views (last 30 days)
Rik
Rik on 4 Oct 2019
Commented: Adam on 10 Feb 2020
There are multiple ways to create a graphical user interface (GUI) in Matlab. Which method is the best depends on multiple factors: the complexity of the project, to what extent it should be a long-term solution, on what releases your GUI should work, your available time, your skill level, and probably other factors I'm forgetting.
To keep the thread clear I'll attempt to provide a short outline a few ways in this question, and leave the details for the answers. (@anyone with editing privileges: feel free to update the section below if I missed something important and am slow in editing this question)
---------------------------------------------------------------------------------------------------
GUIDE
GUIDE is probably the first tool new users will encounter. It is very useful for quickly putting something together, but it is otherwise fairly limited. It requires maintaining (and distributing) both a .m and a .fig file. Note that the GUIDE environment will be removed in a future release. After GUIDE is removed, existing GUIDE apps will continue to run in Matlab but they will not be editable in GUIDE. If you're starting a new GUI, don't use GUIDE. If you're updating an existing GUIDE GUI, migrate it to AppDesigner.
GUILT
Although I haven't had a detailed look myself, it seems a list like this is not complete without at least mentioning the GUI Layout Toolbox, which is available on the file exchange and offers a lot of customization options.
Programmatic GUIs
You can bypass GUIDE and use the normal figures and functions like uicontrol to build GUIs from code. This makes the design less visual, but more flexible for future additions.
App Designer
The official successor to GUIDE, AppDesigner is not based on functions, but works similar to a class. It uses uifigure and mostly uses graphical elements that are incompatible with 'normal' GUIs that are created with a figure (or .fig).

  4 Comments

Show 1 older comment
Rik
Rik on 7 Oct 2019
@Adam, please feel free to add your style of a programmatic class-based GUI to the answer section.
Adam
Adam on 10 Feb 2020
One thing I haven't tried yet, but really need to with the looming death of javacomponent, is doing a programmatic class-based UI based on a uifigure. But this addition from R2018b hopefully makes this a feasible way forward for future GUIs:
As mentioned above, I make heavy use of the GUI Layout Toolbox and would hate to create a complex UI without it now, but the uigridlayout does look like a contender to replace this for use with uifigure, provided Mathworks provide enough of the components I would want to replace using java-based ones (most notably a range slider, though that can be worked around with an ugly 2 slider affair).
I'll no doubt try appdesigner from time to time again, but until or unless the editor behaves the same as the main editor for coding and the entire code for the UI is editable, it's still a non-starter for me.

Sign in to comment.

Answers (3)

Rik
Rik on 4 Oct 2019
A programmatic GUI is ideal if you need a lot of flexibility and many possibilies for support of old releases. It is less visual when designing, but that can be mitigated by either trial and error in a script, or by using GUIDE to design the initial layout and get the Position right for your objects.
My small guide to avoid GUIDE:
  • Make a figure (with f=figure;) and look into the doc for figure which properties you want to turn off (you probably want to set Menu and Toolbar to 'none')
  • Create buttons and axes and everything you need with functions like uicontrol and axes. Save the handles to each element to fields of a struct (like handles.mybutton=uicontrol(___);)
  • Use those handles in function calls that create graphics objects. Never use gcf and gca in your code, because the user might have clicked on a different figure window, making those call targets incorrect. Most functions allow you to specify a parent object (including, plot, bar, axes and many more). When creating a graphics object, check the documentation to see how you can specify the parent.
  • When you've finished loading all data (and saving it to fields of your handles struct), and creating all the buttons, save your handles struct to the guidata of your figure like this guidata(handles.f,handles);. (You can also use getappdata and setappdata)
  • You can set the Callback property of many objects. If you do, use a function name with an @ in front, or a char array that can be evaluated to valid code. (like @MyFunction or 'disp(''you pushed the button'')')
  • Callback functions will be called with two arguments: the first is a handle to the callback object, the second is eventdata that may contain special information. To get access to your data, just use handles=guidata(gcbo);. You can replace the gcbo function with the name of the first input to your callback function if you prefer.
  • More information about callbacks can be found in multiple places in the doc, for example here.

  2 Comments

Rik
Rik on 7 Oct 2019
You do sacrifice compatibility with GNU Octave by using nested functions (although that might not be a concern, I think it is important for it be mentioned).

Sign in to comment.


Adam Danz
Adam Danz on 4 Oct 2019
I think drag-n-drop construction of GUIs is a useful tool, especially for beginners, but it comes with limitations and, in the case of GUIDE, occasional bugs, if not used properly. Unlike drag-n-drop methods, building a GUI from the bottom-up requires a greater investment of time during development but the payoffs are
  • more control of the GUI components
  • easier troubleshooting
  • less overhead (I still get flustered when the GUI initialization runs twice during GUIDE GUI startup)
  • a deeper background knowledge of graphical objects and their properties
  • time saved in the long run by avoiding problems that are otherwise difficult or impossible to trace
Even with a beginner's level of Matlab experience, IMO these resources below are enough to get started in making a gui from the bottom-up.
  1. Know the UI options. The user interface can be created on a figure() or a uifigure(). Each have a different set of components that are not compatible with the other and they both have a different appearance.
  2. For figure() GUIs, interactive components are mainly added using uicontrol() and thier properties are listed here. A list of some of the components that can be included in a figure() GUI is here. Most of the time there are useful examples in the documentation how to implement a UI component, set its properties, and write its callback function. To start, the 'units' and 'position' properties will allow you to position the component within the figure.
  3. For uifigure() GUIs, interactive components are added by their individual functions. A list of uifigure() components is here which includes links to the properties of each component.
  4. Understanding callback functions. Many of the UI components have a wide range of callback functions that are invoked by user interaction. Here is a long list of callback options, the action that invokes them, and the components that allow them. This link provides decent examples of how to define a callback function and how to specify its inputs while this link shows callback function examples for each type of UI component.
It all boils down to being familiar with the list of UI components, their properties, and their callback function options. It may sound like a lot to a beginner but most of the properties and callback functions behave the same between components so the level of understand should accelerate after just a few successfully implemented buttons or sliders.

  0 Comments

Sign in to comment.


Rik
Rik on 30 Jan 2020
Edited: Rik on 31 Jan 2020
Partly inspired by Adam's comment, I created a class wrapping a GUI as an answer to this question. This example contains a radiobutton, an OK button (which only closes the figure), and a cancel button (which reverts all properties back to the initial value and closes the figure). This should show-case the main things you can do with a class-based programmatic GUI.
It style is a subset of programmatic GUIs, so most if not all of the advice in my other answer applies here as well.
classdef MyClass < handle
%Just like with a normal function, the doc goes here, otherwise
%help('MyClass') will grab the help text from the constructor.
properties %public properties
Radio_Selected=false;%set initial value (optional)
end
properties (Access = private) %hide implementation details
fig
defaults=struct;
end
methods
function obj=MyClass
%Contructor method, this creates the GUI
obj.fig=figure('Units','Normalized','Position',[.3 .3 .3 .3],...
'menubar', 'none');
%Store the current value of the public properties as defaults
props=properties(obj);
for n=1:numel(props)
obj.defaults.(props{n})=obj.(props{n});
end
%You can either set a private method as a callback, or use an
%embedded function. Of course an evaluated char also works.
uicontrol('Units','Normalized','Position',[.1 .1 .35 .25],...
'String', 'OK',...
'Callback','close(gcbf)');
uicontrol('Units','Normalized','Position',[.55 .1 .35 .25],...
'String', 'CANCEL',...
'Callback',@(h,e) obj.Callback_CANCEL);
uicontrol('Style','radiobutton', ...
'Units','Normalized','Position', [.1 .5 .8 .4],...
'String','Some radio button',...
'Callback',@(h,e) Callback_radio(h,obj));
end
end
methods( Access = private )
function Callback_CANCEL(obj)
%Revert to defaults and close figure
props=properties(obj);
for n=1:numel(props)
obj.(props{n})=obj.defaults.(props{n});
end
close(obj.fig)
end
end
end
function Callback_radio(h,class_obj)
%Set the class property so it can be retrieved after the GUI has closed
class_obj.Radio_Selected=h.Value;
end

  2 Comments

Adam
Adam on 30 Jan 2020
I would generally use class methods for callback functions, but I guess standalone function with the object explicitly passed in like any other argument works too. I've never actually tried defining functions in the class file, but outside of the classdef, but it does seem to work, and crucially the function still have access to the private data which I had thought wouldn't be the case before I ran your example!
e.g. I would do:
'callback',@(h,e) obj.Callback_OK()
with
methods( Access = private )
function Callback_Ok( obj )
close( obj.fig );
end
end
and all the other callbacks also in that private block. But it amounts to the same thing!
Rik
Rik on 30 Jan 2020
Ah, so that is the 'correct' way to do it, I have edited my example to include that as an option as well. While writing/debugging I was already wondering how I could best set a method as the callback function. I suspect this syntax works at least since R2016b, as that is the release where you could put local functions in a script as well, but I have not tested the extent myself.

Sign in to comment.

Sign in to answer this question.