- Undocumented Matlab - https://undocumentedmatlab.com -

Matlab toolstrip – part 6 (complex controls)

Posted By Yair Altman On January 21, 2019 | 4 Comments

In previous posts [1] I showed how we can create custom Matlab app toolstrips using simple controls such as buttons and checkboxes. Today I will show how we can incorporate more complex controls into our toolstrip: button groups, edit-boxes, spinners, sliders etc.

Some custom Toolstrip Controls

Toolstrips can be a bit complex to develop so I’m proceeding slowly, with each post in the miniseries building on the previous posts. I encourage you to review the earlier posts in the Toolstrip miniseries [1] before reading this post.

The first place to search for potential toostrip components/controls is in Matlab’s built-in toolstrip demos [2]. The showcaseToolGroup demo displays a large selection of generic components grouped by function. These controls’ callbacks do little less than simply output a text message in the Matlab console. On the other hand, the showcaseMPCDesigner demo shows a working demo with controls that interact with some docked figures and their plot axes. The combination of these demos should provide plenty of ideas for your own toolstrip implementation. Their m-file source code is available in the %matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+desktop/ folder. To see the available toolstrip controls in action and how they could be integrated, refer to the source-code of these two demos.
All toolstrip controls are defined by classes in the %matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+toolstrip/ folder and use the matlab.ui.internal.toolstrip package prefix, for example:

% Alternative 1:
hButton = matlab.ui.internal.toolstrip.Button;
% Alternative 2:
import matlab.ui.internal.toolstrip.*
hButton = Button;

For the remainder of today’s post it is assumed that you are using one of these two alternatives whenever you access any of the toolstrip classes.

Top-level toolstrip controls

Control Description Important properties Callbacks Events
EmptyControl Placeholder (filler) in container column (none) (none) (none)
Label Simple text label (no action) Icon, Text (string) (none) (none)
Button Push-button Icon, Text (string) ButtonPushedFcn ButtonPushed
ToggleButton Toggle (on/off) button Icon, Text (string), Value (logical true/false), ButtonGroup (a ButtonGroup object) ValueChangedFcn ValueChanged
RadioButton Radio-button (on/off) Text (string), Value (logical true/false), ButtonGroup (a ButtonGroup object) ValueChangedFcn ValueChanged
CheckBox Check-box (on/off) Text (string), Value (logical true/false) ValueChangedFcn ValueChanged
EditField Single-line editbox Value (string) ValueChangedFcn ValueChanged, FocusGained, FocusLost
TextArea Multi-line editbox Value (string) ValueChangedFcn ValueChanged, FocusGained, FocusLost
Spinner A numerical spinner control of values between min,max Limits ([min,max]), StepSize (integer), NumberFormat (‘integer’ or ‘double’), DecimalFormat (string), Value (numeric) ValueChangedFcn ValueChanged, ValueChanging
Slider A horizontal slider of values between min,max Limits ([min,max]), Labels (cell-array), Ticks (integer), UseSmallFont (logical true/false, R2018b onward), ShowButton (logical true/false, undocumented), Steps (integer, undocumented), Value (numeric) ValueChangedFcn ValueChanged, ValueChanging
ListBox List-box selector with multiple items Items (cell-array), SelectedIndex (integer), MultiSelect (logical true/false), Value (cell-array of strings) ValueChangedFcn ValueChanged
DropDown Single-selection drop-down (combo-box) selector Items (cell-array), SelectedIndex (integer), Editable (logical true/false), Value (string) ValueChangedFcn ValueChanged
DropDownButton Button that has an associated drop-down selector Icon, Text (string), Popup (a PopupList object) DynamicPopupFcn (none)
SplitButton Split button: main clickable part next to a drop-down selector Icon, Text (string), Popup (a PopupList object) ButtonPushedFcn, DynamicPopupFcn ButtonPushed, DropDownPerformed (undocumented)
Gallery A gallery of selectable options, displayed in-panel MinColumnCount (integer), MaxColumnCount (integer), Popup (a GalleryPopup object), TextOverlay (string) (none) (none)
DropDownGalleryButton A gallery of selectable options, displayed as a drop-down MinColumnCount (integer), MaxColumnCount (integer), Popup (a GalleryPopup object), TextOverlay (string) (none) (none)

In addition to the control properties listed in the table above, all toolstrip controls share some common properties:

  • Description – a string that is shown in a tooltip when you hover the mouse over the control
  • Enabled – a logical value (default: true) that controls whether we can interact with the control. A disabled control is typically grayed-over. Note that the value is a logical true/false, not ‘on’/’off’
  • Tag – a string that can be used to uniquely identify/locate the control via their container’s find(tag) and findAll(tag) methods. Can contain spaces and special symbols – does not need to be a valid Matlab identifier
  • Children – contains a list of sub-component (if any); useful with complex controls
  • Parent – the handle of the container that contains the control
  • Type – the type of control, typically its class-name
  • Mnemonic – an undocumented string property, currently unused (?)
  • Shortcut – an undocumented string property, currently unused (?)

The EmptyControl, Button, ToggleButton and CheckBox controls were discussed in an earlier post [3] of this miniseries. The bottom 6 selection controls (ListBox, DropDown, DropDownButton, SplitButton, Gallery and DropDownGalleryButton) will be discussed in the next post. The rest of the controls are described below.

Button groups

A ButtonGroup binds several CheckBox and ToggleButton components such that only one of them is selected (pressed) at any point in time. For example:

hSection = hTab.addSection('Radio-buttons');
hColumn = hSection.addColumn();
% Grouped RadioButton controls
hButtonGroup = ButtonGroup;
hRadio = RadioButton(hButtonGroup, 'Option choice #1');
hRadio.ValueChangedFcn = @ValueChangedCallback;
hColumn.add(hRadio);
hRadio = RadioButton(hButtonGroup, 'Option choice #2');
hRadio.ValueChangedFcn = @ValueChangedCallback;
hRadio.Value = true;
hColumn.add(hRadio);

Toolstrip ButtonGroup
Toolstrip ButtonGroup
Note that unlike the uibuttongroup object in “standard” figure GUI, the toolstrip’s ButtonGroup object does not have a SelectionChangedFcn callback property (or corresponding event). Instead, we need to set the ValueChangedFcn callback property (or listen to the ValueChanged event) separately for each individual control. This is really a shame – I think it would make good design sense to have a SelectionChangedFcn callback at the ButtonGroup level, as we do for uibuttongroup (in addition to the individual control callbacks).
Also note that the internal documentation of ButtonGroup has an error – it provides an example usage with RadioButton that has its constructor inputs switched: the correct constructor is RadioButton(hButtonGroup,labelStr). On the other hand, for ToggleButton, the hButtonGroup input is the [optional] 3rd input arg of the constructor: ToggleButton(labelStr,Icon,hButtonGroup). I think that it would make much more sense for the RadioButton constructor to follow the documentation and the style of ToggleButton and make the hButtonGroup input the last (2nd, optional) input arg, rather than the 1st. In other words, it would make more sense for RadioButton(labelStr,hButtonGroup), but unfortunately this is currently not the case.

Label, EditField and TextArea

A Label control is a simple non-clickable text label with an optional Icon, whose text is controlled via the Text property. The label’s alignment is controlled by the containing column’s HorizontalAlignment property.
An EditField is a single-line edit-box. Its string contents can be fetched/updated via the Value property, and when the user updates the edit-box contents the ValueChangedFcn callback is invoked (upon each modification of the string, i.e. every key-click). This is a pretty simple control actually.
The EditField control has a hidden (undocumentented) settable property called PlaceholderText, which presumably aught to display a gray initial prompt within the editbox. However, as far as I could see this property has no effect (perhaps, as the name implies, it is a place-holder for a future functionality…).
A TextArea is another edit-box control, but enables entering multiple lines of text, unlike EditField which is a single-line edit-box. TextArea too is a very simple control, having a settable Value string property and a ValueChangedFcn callback. Whereas EditField controls, being single-line, would typically be included in 2- or 3-element toolstrip columns, the TextArea would typically be placed in a single-element column, so that it would span the entire column height.
A peculiarity of toolstrip columns is that unless you specify their Width property, the internal controls are displayed with a minimal width (the width is only controllable at the column level, not the control-level). This is especially important with EditField and TextArea controls, which are often empty by default, causing their assigned width to be minimal (only a few pixels). This is corrected by setting their containing column’s Width:

% EditField controls
column1 = hSection.addColumn('HorizontalAlignment','right');
column1.add(Label('Yaba:'))
column1.add(Label('Daba doo:'))
column2 = hSection.addColumn('Width',70);
column2.add(EditField);
column2.add(EditField('Initial text'));
% TextArea control
column3 = hSection.addColumn('Width',90);
hEdit = TextArea;
hEdit.ValueChangedFcn = @ValueChangedCallback;
column3.add(hEdit);

Toolstrip Label, EditField and TextArea
Toolstrip Label, EditField and TextArea

Spinner

Spinner is a single-line numeric editbox that has an attached side-widget where you can increase/decrease the editbox value by a specified amount, subject to predefined min/max values. If you try to enter an illegal value, Matlab will beep and the editbox will revert to its last acceptable value. You can only specify a NumberFormat of ‘integer’ or ‘double’ (default: ‘integer’) and a DecimalFormat which is a string composed of the number of sub-decimal digits to display and the format (‘e’ or ‘f’). For example, DecimalFormat=’4f’ will display 4 digits after the decimal in floating-point format (‘e’ means engineering format). Here is a short usage example (notice the different ways that we can set the callbacks):

hColumn = hSection.addColumn('Width',100);
% Integer spinner (-100 : 10 : 100)
hSpinner = Spinner([-100 100], 0);  % [min,max], initialValue
hSpinner.Description = 'this is a tooltip description';
hSpinner.StepSize = 10;
hSpinner.ValueChangedFcn = @ValueChangedCallback;
hColumn.add(hSpinner);
% Floating-point spinner (-10 : 0.0001 : 10)
hSpinner = Spinner([-10 10], pi);  % [min,max], initialValue
hSpinner.NumberFormat = 'double';
hSpinner.DecimalFormat = '4f';
hSpinner.StepSize = 1e-4;
addlistener(hSpinner,'ValueChanged', @ValueChangedCallback);
addlistener(hSpinner,'ValueChanging',@ValueChangingCallback);
hColumn.add(hSpinner);

Toolstrip Spinner
Toolstrip Spinner
A logical extension of the toolstrip spinner implementation would be for non-numeric spinners [4], as well as custom Value display formatting. Perhaps this will become available at some future Matlab release.

Slider

Slider is a horizontal ruler on which you can move a knob from the left (min Value) to the right (max Value). The ticks and labels are optional and customizable. Here is a simple example showing a plain slider (values between 0-100, initial value 70, ticks every 5, labels every 20, step size 1), followed by a custom slider (notice again the different ways that we can set the callbacks):

hColumn = hSection.addColumn('Width',200);
hSlider = Slider([0 100], 70);  % [min,max], initialValue
hSlider.Description = 'this is a tooltip';
tickVals = 0 : 20 : 100;
hSlider.Labels = [compose('%d',tickVals); num2cell(tickVals)]';  % {'0',0; '20',20; ...}
hSlider.Ticks = 21;  % =numel(0:5:100)
hSlider.ValueChangedFcn = @ValueChangedCallback;
hColumn.add(hSlider);
hSlider = Slider([0 100], 40);  % [min,max], initialValue
hSlider.Labels = {'Stop' 0; 'Slow' 20; 'Fast' 50; 'Too fast' 75; 'Crash!' 100};
try hSlider.UseSmallFont = true; catch, end  % UseSmallFont was only added in R2018b
hSlider.Ticks = 11;  % =numel(0:10:100)
addlistener(hSlider,'ValueChanged', @ValueChangedCallback);
addlistener(hSlider,'ValueChanging',@ValueChangingCallback);
hColumn.add(hSlider);

Toolstrip Slider
Toolstrip Slider

Toolstrip miniseries roadmap

The next post will discuss complex selection components, including listbox, drop-down, split-button, and gallery.
Following that, I plan to discuss toolstrip collapsibility, the ToolPack framework, docking layout, DataBrowser panel, QAB (Quick Access Bar), underlying Java controls, and adding toolstrips to figures – not necessarily in this order.
Matlab toolstrips can be a bit complex, so I plan to proceed in small steps, each post building on top of its predecessors.
If you would like me to assist you in building a custom toolstrip or GUI for your Matlab program, please let me know [5].

Categories: Figure window, GUI, High risk of breaking in future versions, UI controls, Undocumented feature


4 Comments (Open | Close)

4 Comments To "Matlab toolstrip – part 6 (complex controls)"

#1 Comment By Yaroslav On January 24, 2019 @ 14:21

Hi @Yair,

First of all — kudos on the amazing work. I think that the miniseries format has a substantial added-value, as one can connect the different pieces to a complete picture.

Regarding this series: do you think Mathworks(c) will incorporate the toolstrip widgets into appdesigner? I mean, for the lazy—but advanced—user, it seems only natural to have these widgets without all the nuisance of placing them programmatically.

— Yaroslav

#2 Comment By Yair Altman On January 24, 2019 @ 14:57

@Yaroslav – thanks.

It would indeed make a lot of sense for the toolstrip functionality to be added to App Designer, but I don’t know whether this is what MW plans or what the timeline for this might be. For example, MW might one day release a separate independent tool to create apps (with toolstrip, Data Browser, docking etc.), that would not be a direct part of App Designer. In fact, MW might even decide to monetize such a tool as a new commercial toolbox. I hope MW won’t do this, but it would certainly be just as viable an option as adding the functionality to App Designer. Unfortunately, I see a hint for this direction in the fact that the Matlab Compiler Toolbox is currently prevented from compiling and deploying user-created apps (only figures and uifigures). Maybe it’s related and maybe not – I hope not, but I fear that it may indeed be. In such a case, I hope that MW would at least leave the programmatic route as part of the core Matlab.

In any case, I believe it is unlikely that the toolstrip functionality will ever be added to GUIDE, because I believe MW plans to discontinue GUIDE as soon as it can get the new uifigures and App Designer to a reasonable state. This is the reason that MW never added uitree to GUIDE, only to App Designer. And this is entirely understandable, after all most R&D resources should be devoted to future tools, not outdated ones (and GUIDE is at least a decade, if not two, out-of-date). Therefore, for legacy (Java-based) figures and apps, the only option for adding a toolstrip would be programmatically.

#3 Comment By Batuhan On January 29, 2019 @ 15:16

Hello @Yair,

I was able to compile my ToolGroup app however the standalone executable would only open for a second display itself and then shutdown immediately. I believe this is because Compiler is prevented to do things correctly when it comes to ToolGroup apps. Or could I have done something wrong?

Thank you.

#4 Comment By Yair Altman On February 15, 2019 @ 00:54

@Batuhan – I don’t know what exactly happened in your specific case, but it could be that you are not storing the ToolGroup handle in a persistent location, as I explained in [12] at the beginning of this miniseries:

“An annoying quirk with ToolGroups is that they automatically close when their reference handle is deleted from Matlab memory. The specific behavior changes depending on the contents of the container and the Matlab release, but in general it’s safest to preserve the hToolGroup variable, to prevent the window from closing, when this variable goes out of scope, when the function (in which we create the ToolGroup) returns. …”


Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/matlab-toolstrip-part-6-complex-controls

URLs in this post:

[1] previous posts: https://undocumentedmatlab.com/blog/tag/toolstrip

[2] Matlab’s built-in toolstrip demos: https://undocumentedmatlab.com/blog/matlab-toolstrip-part-1

[3] earlier post: https://undocumentedmatlab.com/blog/matlab-toolstrip-part-3-basic-customization#example

[4] non-numeric spinners: https://undocumentedmatlab.com/blog/using-spinners-in-matlab-gui

[5] please let me know: https://undocumentedmatlab.com/consulting

[6] Matlab toolstrip – part 7 (selection controls) : https://undocumentedmatlab.com/articles/matlab-toolstrip-part-7-selection-controls

[7] Matlab toolstrip – part 4 (control customization) : https://undocumentedmatlab.com/articles/matlab-toolstrip-part-4-control-customization

[8] Matlab toolstrip – part 8 (galleries) : https://undocumentedmatlab.com/articles/matlab-toolstrip-part-8-galleries

[9] Matlab toolstrip – part 3 (basic customization) : https://undocumentedmatlab.com/articles/matlab-toolstrip-part-3-basic-customization

[10] Matlab toolstrip – part 2 (ToolGroup App) : https://undocumentedmatlab.com/articles/matlab-toolstrip-part-2-toolgroup-app

[11] Matlab toolstrip – part 9 (popup figures) : https://undocumentedmatlab.com/articles/matlab-toolstrip-part-9-popup-figures

[12] : https://undocumentedmatlab.com/blog/matlab-toolstrip-part-2-toolgroup-app#ToolGroup

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.