When designing Matlab applications, we can either use Matlab’s designer (guide), or manually position each GUI component programmatically, using its Position property. Matlab lacks the layout managers so common in Java, that enable easy relative component positioning, taking into account dynamic container size, components spacing weights etc. Of course, we can always trap the container’s ResizeFcn callback to update our layout, but doing so is one royal pain in the so-and-so…
Luckily, there is (of course) an undocumented solution to this problem, and at the public’s demand I will detail it below. It doesn’t solve all layout-management needs, but it goes a long way. Most importantly, it uses pure Matlab – no Java knowledge whatsoever is needed.
Matlab’s uicontainer family (uicontainer, uiflowcontainer and uigridcontainer) consists of container objects that enable customizable layout management of contained components. Uicontainers can contain any Matlab component that may have a uipanel handle as a Parent property. This includes uicontrols, plot axes etc., as well as other uicontainers.
The basic uicontainer object appears to be little more than a transparent container for contained objects. It can be used interchangeably with uipanel, which appears to be a specialized type of a uicontainer. Indeed, as MathWorks notes:
The UICONTAINER function is undocumented, and is not intended for direct use. The UIPANEL function should be used instead, as it provides more functionality.
In some cases, Matlab itself uses uicontainer instead of uipanel: for example, ActiveX controls are enclosed within transparent uicontrol objects when added to a figure:
>> [hActivex,hContainer] = actxcontrol('OWC11.Spreadsheet.11'); >> get(hContainer,'Type') ans = uicontainer
uicontainer objects are not very customizable. For example, unlike uipanels, uicontainers have no titles or borders. We would therefore usually prefer to use uipanels, as Mathworks suggested above. An exception to this rule is a case where we need to derive our own customized container class. An example of this is found in %matlabroot%/toolbox/matlab/uitools/@uitools/@uitab/schema.m, which derives uicontainer to create a uitab container (which will be described in a future article).
Relatives of uicontainer are more useful in general: uiflowcontainers and uigridcontainers act similarly to Java’s layout managers – specifically, FlowLayout and GridLayout. I expect to see additional layout managers incarnated within Matlab uicontainers in future Matlab versions (perhaps in the R2010b pre-release that came out today – I can’t wait to see…).
uiflowcontainer is a uicontainer that enables adding uicontrols to the container without specifying an exact position as would be required for uicontainer or uipanel (actually, positions may be specified, but they are simply ignored).
By default, objects are added from the top-left corner, depending on the uiflowcontainer‘s available space and dimensions: if width > height then rightward, otherwise downward. If the container’s dimensions change, for example by resizing the figure window, then the container’s components will automatically be resized accordingly:
hc = uiflowcontainer('Units','norm','Position',[.1,.1,.8,.8]); h1 = uicontrol('string','1','parent',hc); h2 = uicontrol('string','2','parent',hc); h3 = uicontrol('string','3','parent',hc);
The components flow direction within the container may be modified by setting the uiflowcontainer‘s FlowDirection property from its default value of ‘Auto’ to ‘AutoReverse’, ‘BottomUp’, ‘TopDown’, ‘LeftToRight’, or ‘RightToLeft’:
Spacing between the components and the container’s border, and between themselves, may be controlled via the Margin property. By default, Margin is set to 2 (pixels):
The advantage of using uiflowcontainer is its automatic resizing and positioning of components. Notice how we simply specified the uiflowcontainer‘s handle as the control’s parent, and got all this functionality out-of-the-box!
If we need to use fixed-dimensions components, we can use uicontrol‘s undocumented properties HeightLimits and WidthLimits, each of which is a 2-element numeric vector specifying the minimal and maximal allowed value for the height or width (both of these vectors are [2,Inf] by default). uiflowcontainer tries to accommodate the requested limits by stretching or compressing its components (we need to resize the figure for the component resizing to become visible):
set(h1, 'HeightLimits',[10,20], 'WidthLimits',[30,30]) set(h2, 'HeightLimits',[50,50]) set(h3, 'HeightLimits',[2,inf]) % =default value
Sometimes, however, no amount of component resizing is enough to fully contain all components within the uiflowcontainer:
set(h2, 'WidthLimits',[150,150]) set(h3, 'HeightLimits',[50,50]) % resize figure to see effect
Note: uiflowcontainer normally ignores the specified limits if they would cause the component to stretch beyond the container boundaries. This happens unless the limits are identical (as in the preceding example), which informs uiflowcontainer that it has no judgment in the component’s dimensions.
In more complex cases, consider coding your own customized class deriving from uiflowcontainer. An example for such a customization can be seen in %matlabroot%/toolbox/matlab/uitools/@uitools/@uitabgroup/schema.m, which derives uiflowcontainer to create a uitabgroup container.
Components within a uiflowcontainer are ordered according to the order they were added to the container. This order can be modified by rearranging the handles in the container’s Children property, or by using the uistack function which does the same. Note that a side-effect of this is that the components dimensions are re-normalized:
uigridcontainer is similar to uiflowcontainer in its specialization of the layout: in this case, the container is divided into a transparent grid of N-by-M cells (1×1 by default), each of which can contain its own component:
hc = uigridcontainer('Units','norm','Position',[.1,.1,.8,.8]); set(hc, 'GridSize',[2,3]); % default GridSize is [1,1] h1 = uicontrol('string','1','parent',hc); h2 = uicontrol('string','2','parent',hc); h3 = uicontrol('string','3','parent',hc); h4 = axes('parent',hc); x = -4:.1:4; plot(h4,x,sin(x)); set(h4,'YTickLabel',,'XTickLabel',);
The grid cells relative size can be controlled via the HorizontalWeight and VerticalWeight properties (set to NaN by default). These properties should be a numeric vector the same size as the corresponding number of cells. The property values are not important – only their relative values are used to control the relative cell dimensions. The EliminateEmptySpace property (default=’off’) controls whether empty grid rows/columns are eliminated from the container or displayed. As in uiflowcontainers, the Margin property controls the spacing between the internal components and borders:
set(hc, 'HorizontalWeight',[6,3,1], 'VerticalWeight',[0.2,0.5]) delete([h2,h3]); % only h1,h4 remain set(hc,'EliminateEmptySpace','on')
Other layout alternatives
Brad Phelan of XTargets has created Matlab equivalents of Java’s BorderLayout and SpringLayout. The advantage of using Brad’s layout managers is that they appear to have full Matlab interoperability, including the ability to add Matlab components, unlike Java’s layout managers.
Additional and more flexible layout managers are available in Java (one of my favorites is JGoodies Forms, which is pre-bundled with Matlab). Just remember the limitation that no Matlab component (such as GUI controls or plot axes) can be added to Java containers. Therefore, feel free to use these Java containers as long as their contained GUI is limited to Java components (JButton, JComboBox etc.).
Today’s article about uicontainer and its relatives was the first of several posts that will describe undocumented functions that reside in the %matlabroot%/toolbox/matlab/uitools folder. Feel free to look within this folder for other interesting undocumented functions. Most of these functions are semi-documented, meaning that they have a usable help-section hidden within the m-file (type “edit uicontainer.m” for example). Many have existed more-or-less unchanged for many releases. Note that there is no guarantee that they will remain in future releases. When using unsupported functionality, always code defensively.