Undocumented Matlab https://undocumentedmatlab.com Charting Matlab's unsupported hidden underbelly Mon, 21 Jan 2019 16:00:38 +0000 en-US hourly 1 https://wordpress.org/?v=4.4.1 Matlab toolstrip – part 6 (complex controls)https://undocumentedmatlab.com/blog/matlab-toolstrip-part-6-complex-controls https://undocumentedmatlab.com/blog/matlab-toolstrip-part-6-complex-controls#respond Mon, 21 Jan 2019 16:00:38 +0000 https://undocumentedmatlab.com/?p=8235
 
Related posts:
  1. Matlab toolstrip – part 4 (control customization) Matlab toolstrip components (controls) can be customized in various ways, including user-defined callbacks. ...
  2. Matlab toolstrip – part 2 (ToolGroup App) Matlab users can create custom Apps with toolstrips and docked figures. ...
  3. Matlab toolstrip – part 3 (basic customization) Matlab toolstrips can be created and customized in a variety of ways. ...
  4. FindJObj – find a Matlab component’s underlying Java object The FindJObj utility can be used to access and display the internal components of Matlab controls and containers. This article explains its uses and inner mechanism....
 
]]>
In previous posts 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 before reading this post.

The first place to search for potential toostrip components/controls is in Matlab’s built-in toolstrip demos. 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

ControlDescriptionImportant propertiesCallbacksEvents
EmptyControlPlaceholder (filler) in container column(none)(none)(none)
LabelSimple text label (no action)Icon, Text (string)(none)(none)
ButtonPush-buttonIcon, Text (string)ButtonPushedFcnButtonPushed
ToggleButtonToggle (on/off) buttonIcon, Text (string), Value (logical true/false), ButtonGroup (a ButtonGroup object)ValueChangedFcnValueChanged
RadioButtonRadio-button (on/off)Text (string), Value (logical true/false), ButtonGroup (a ButtonGroup object)ValueChangedFcnValueChanged
CheckBoxCheck-box (on/off)Text (string), Value (logical true/false)ValueChangedFcnValueChanged
EditFieldSingle-line editboxValue (string)ValueChangedFcnValueChanged, FocusGained, FocusLost
TextAreaMulti-line editboxValue (string)ValueChangedFcnValueChanged, FocusGained, FocusLost
SpinnerA numerical spinner control of values between min,maxLimits ([min,max]), StepSize (integer), NumberFormat (‘integer’ or ‘double’), DecimalFormat (string), Value (numeric)ValueChangedFcnValueChanged, ValueChanging
SliderA horizontal slider of values between min,maxLimits ([min,max]), Labels (cell-array), Ticks (integer), UseSmallFont (logical true/false, R2018b onward), ShowButton (logical true/false, undocumented), Steps (integer, undocumented), Value (numeric)ValueChangedFcnValueChanged, ValueChanging
ListBoxList-box selector with multiple itemsItems (cell-array), SelectedIndex (integer), MultiSelect (logical true/false), Value (cell-array of strings)ValueChangedFcnValueChanged
DropDownSingle-selection drop-down (combo-box) selectorItems (cell-array), SelectedIndex (integer), Editable (logical true/false), Value (string)ValueChangedFcnValueChanged
DropDownButtonButton that has an associated drop-down selectorIcon, Text (string), Popup (a PopupList object)DynamicPopupFcn(none)
SplitButtonSplit button: main clickable part next to a drop-down selectorIcon, Text (string), Popup (a PopupList object)ButtonPushedFcn, DynamicPopupFcnButtonPushed, DropDownPerformed (undocumented)
GalleryA gallery of selectable options, displayed in-panelMinColumnCount (integer), MaxColumnCount (integer), Popup (a GalleryPopup object), TextOverlay (string)(none)(none)
DropDownGalleryButtonA gallery of selectable options, displayed as a drop-downMinColumnCount (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 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, 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 R2018a
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.

]]>
https://undocumentedmatlab.com/blog/matlab-toolstrip-part-6-complex-controls/feed 0
Matlab toolstrip – part 5 (icons)https://undocumentedmatlab.com/blog/matlab-toolstrip-part-5-icons https://undocumentedmatlab.com/blog/matlab-toolstrip-part-5-icons#respond Sun, 06 Jan 2019 17:00:37 +0000 https://undocumentedmatlab.com/?p=8188
 
Related posts:
  1. FindJObj GUI – display container hierarchy The FindJObj utility can be used to present a GUI that displays a Matlab container's internal Java components, properties and callbacks....
  2. Plot-type selection components Several built-in components enable programmatic plot-type selection in Matlab GUI - this article explains how...
  3. Animated busy (spinning) icon An animated spinning icon label can easily be embedded in Matlab GUI. ...
  4. Auto-completion widget Matlab includes a variety of undocumented internal controls that can be used for an auto-completion component. ...
 
]]>
In a previous post I showed how we can create custom Matlab app toolstrips. Toolstrips can be a bit complex to develop so I’m trying to proceed slowly, with each post in the miniseries building on the previous posts. I encourage you to review the earlier posts in the Toolstrip miniseries before reading this post. Today’s post describes how we can set various icons, based on the toolstrip created in the previous posts:
Toolstrip example (basic controls)

Toolstrip example (basic controls)


Component icons

Many toolstrip controls (such as buttons, but not checkboxes for example) have a settable Icon property. The standard practice is to use a 16×16 icon for a component within a multi-component toolstrip column (i.e., when 2 or 3 components are displayed on top of each other), and a 24×24 icon for a component that spans the entire column height (i.e., when the column contains only a single component).

We can use one of the following methods to specify the icon. Note that you need to import matlab.ui.internal.toolstrip.* if you wish to use the Icon class without the preceding package name.

  • The Icon property value is typically empty ([]) by default, meaning that no icon is displayed.
     
  • We can use one of ~150 standard icons using the format Icon.<icon-name>. For example: icon = Icon.REFRESH_24. These icons typically come in 2 sizes: 16×16 pixels (e.g. Icon.REFRESH_16) that we can use with the small-size components (which are displayed when the column has 2-3 controls), and 24×24 pixels (e.g. REFRESH_24) that we can use with the large-size components (which are displayed when the column contains only a single control). You can see the list of the standard icons by running
    matlab.ui.internal.toolstrip.Icon.showStandardIcons

    Standard toolstrip control Icons
  • We can use the Icon constructor by specifying the full filepath for any PNG or JPG image file. Note that other file type (such as GIF) are not supported by this method. For example:
    icon = Icon(fullfile(matlabroot,'toolbox','matlab','icons','tool_colorbar.png')); % PNG/JPG image file (not GIF!)

    In fact, the ~150 standard icons above use this mechanism under the hood: Icon.REFRESH_24 is basically a public static method of the Icon class, which simply calls Icon('REFRESH_24','Refresh_24') (note the undocumented use of a 2-input Icon constructor). This method in turn uses the Refresh_24.png file in Matlab’s standard toolstrip resources folder: %matlabroot%/toolbox/shared/controllib/general/resources/toolstrip_icons/Refresh_24.png.

  • We can also use the Icon constructor by specifying a PNG or JPG file contained within a JAR file, using the standard jar:file:...jar!/ notation. There are numerous icons included in Matlab’s JAR files – simply open these files in WinZip or WinRar and browse. In addition, you can include images included in any external JAR file. For example:
    icon = Icon(['jar:file:/' matlabroot '/java/jar/mlwidgets.jar!/com/mathworks/mlwidgets/actionbrowser/resources/uparrow.png']);
  • We can also use the Icon constructor by specifying a Java javax.swing.ImageIcon object. Fortunately we can create such objects from a variety of image formats (including GIFs). For example:
    iconFilename = fullfile(matlabroot,'toolbox','matlab','icons','boardicon.gif');
    jIcon = javax.swing.ImageIcon(iconFilename);  % Java ImageIcon from file (inc. GIF)
    icon = Icon(jIcon);

    If we need to resize the Java image (for example, from 16×16 to 24×24 or vise versa), we can use the following method:

    % Resize icon to 24x24 pixels
    jIcon = javax.swing.ImageIcon(iconFilename);  % get Java ImageIcon from file (inc. GIF)
    jIcon = javax.swing.ImageIcon(jIcon.getImage.getScaledInstance(24,24,jIcon.getImage.SCALE_SMOOTH))  % resize to 24x24
    icon = Icon(jIcon);
  • We can apparently also use a CSS class-name to load images. This is only relevant for the JavaScript-based uifigures, not legacy Java-based figures that I discussed so far. Perhaps I will explore this in some later post that will discuss toolstrip integration in uifigures.

App window icon

The app window’s icon can also be set. By default, the window uses the standard Matlab membrane icon (%matlabroot%/toolbox/matlab/icons/matlabicon.gif). This can be modified using the hToolGroup.setIcon method, which currently [R2018b] expects a Java ImageIcon object as input. For example:

iconFilename = fullfile(matlabroot,'toolbox','matlab','icons','reficon.gif');
jIcon = javax.swing.ImageIcon(iconFilename);
hToolGroup.setIcon(jIcon)

This icon should be set before the toolgroup window is shown (hToolGroup.open).

Custom app window icon

Custom app window icon

An odd caveat here is that the icon size needs to be 16×16 – setting a larger icon results in the icon being ignored and the default Matlab membrane icon used. For example, if we try to set ‘boardicon.gif’ (16×17) instead of ‘reficon.gif’ (16×16) we’d get the default icon instead. If our icon is too large, we can resize it to 16×16, as shown above:

% Resize icon to 16x16 pixels
jIcon = javax.swing.ImageIcon(iconFilename);  % get Java ImageIcon from file (inc. GIF)
jIcon = javax.swing.ImageIcon(jIcon.getImage.getScaledInstance(16,16,jIcon.getImage.SCALE_SMOOTH))  % resize to 16x16
hToolGroup.setIcon(jIcon)

It’s natural to expect that hToolGroup, which is a pure-Matlab MCOS wrapper class, would have an Icon property that accepts Icon objects, just like for controls as described above. For some reason, this is not the case. It’s very easy to fix it though – after all, the Icon class is little more than an MCOS wrapper class for the underlying Java ImageIcon (not exactly, but close enough). Adapting ToolGroup‘s code to accept an Icon is quite easy, and I hope that MathWorks will indeed implement this in a near-term future release. I also hope that MathWorks will remove the 16×16 limitation, or automatically resize icons to 16×16, or at the very least issue a console warning when a larger icon is specified by the user. Until then, we can use the setIcon(jImageIcon) method and take care to send it the 16×16 ImageIcon object that it expects.

Toolstrip miniseries roadmap

The next post will discuss complex components, including button-group, drop-down, listbox, split-button, slider, popup form, gallery etc.

Following that, my plan is 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.

]]>
https://undocumentedmatlab.com/blog/matlab-toolstrip-part-5-icons/feed 0
Matlab toolstrip – part 4 (control customization)https://undocumentedmatlab.com/blog/matlab-toolstrip-part-4-control-customization https://undocumentedmatlab.com/blog/matlab-toolstrip-part-4-control-customization#respond Sun, 30 Dec 2018 16:00:00 +0000 https://undocumentedmatlab.com/?p=8110
 
Related posts:
  1. Matlab toolstrip – part 6 (complex controls) Multiple types of customizable controls can be added to Matlab toolstrips...
  2. Figure window customizations Matlab figure windows can be customized in numerous manners using the underlying Java Frame reference. ...
  3. Matlab toolstrip – part 2 (ToolGroup App) Matlab users can create custom Apps with toolstrips and docked figures. ...
  4. Matlab toolstrip – part 3 (basic customization) Matlab toolstrips can be created and customized in a variety of ways. ...
 
]]>
In a previous post I showed how we can create custom Matlab app toolstrips. Toolstrips can be a bit complex to develop so I’m trying to proceed slowly, with each post in the miniseries building on the previous posts. I encourage you to review the earlier posts in the Toolstrip miniseries before reading this post. In today’s post we continue the discussion of the toolstrip created in the previous post:
Toolstrip example (basic controls)

Toolstrip example (basic controls)

Today’s post will show how to attach user-defined functionality to toolstrip components, as well as some additional customizations. At the end of today’s article, you should be able to create a fully-functional custom Matlab toolstrip. Today’s post will remain within the confines of a Matlab “app”, i.e. a tool-group that displays docked figures. Future posts will discuss lower-level toolstrip mechanisms, that enable advanced customizations as well as integration in legacy (Java-based, even GUIDE-created) Matlab figures.

Control callbacks

Controls are useless without settable callbacks that affect the program state based on user interactions. There are two different mechanisms for setting callbacks for Matlab toolstrip controls. Refer to the example in the previous post:

  1. Setting the control’s callback property or properties – the property names differ across components (no, for some reason it’s never as simple as Callback in standard uicontrols). For example, the main action callback for push-buttons is ButtonPushedFcn, for toggle-buttons and checkboxes it’s ValueChangedFcn and for listboxes it’s . Setting the callback is relatively easy:
    hColorbar.ValueChangedFcn = @toggleColorbar;
     
    function toggleColorbar(hAction,hEventData)
        if hAction.Selected
            colorbar;
        else
            colorbar('off');
        end
    end

    The hAction object that is passed to the callback function as the first input arg contains various fields of interest, but for some reason the most important object property (Value) is renamed as the Selected property (most confusing). Also, a back-reference to the originating control (hColorbar in this example), which is important for many callbacks, is also missing (and no – I couldn’t find it in the hidden properties either):

    >> hAction
    hAction = 
      Action with properties:
     
                Description: 'Toggle colorbar display'
                    Enabled: 1
                   Shortcut: ''
                   Selected: 1
            QuickAccessIcon: []
        SelectionChangedFcn: @toggleColorbar
                       Text: 'Colorbar'
            IsInQuickAccess: 0
                ButtonGroup: []
                       Icon: [1×1 matlab.ui.internal.toolstrip.Icon]
     
    >> hEventData
    hEventData = 
      ToolstripEventData with properties:
     
        EventData: [1×1 struct]
           Source: [0×0 handle]
        EventName: ''
     
    >> hEventData.EventData
    ans = 
      struct with fields:
     
        Property: 'Value'
        NewValue: 1
        OldValue: 0

    Note that hEventData.Source is an empty handle for some unknown reason.

    The bottom line is that to reference the button state using this callback mechanism we need to either:

    1. Access hAction‘s Selected property which stands-in for the originating control’s Value property (this is what I have shown in the short code snippet above)
    2. Access hEventData.EventData and use its reported Property, NewValue and OldValue fields
    3. Pass the originating control handle as an extra (3rd) input arg to the callback function, and then access it from within the callback. For example:
      hColorbar.ValueChangedFcn = {@toggleColorbar, hColorbar};
       
      function toggleColorbar(hAction,hEventData,hButton)
          if hButton.Value %hAction.Selected
              colorbar;
          else
              colorbar('off');
          end
      end
  2. As an alternative, we can use the addlistener function to attach a callback to control events. Practically all toolstrip components expose public events that can be listened-to using this mechanism. In most cases the control’s callback property name(s) closely follow the corresponding events. For example, for buttons we have the ValueChanged event that corresponds to the ValueChangedFcn property. We can use listeners as follows:
    hCheckbox.addlistener('ValueChanged',@toggleLogY);
     
    function toggleLogY(hCheckbox,hEventData)
        if hCheckbox.Value, type = 'log'; else, type = 'linear'; end
        set(gca, 'XScale',type, 'YScale',type, 'ZScale',type);
    end

    Note that when we use the addlistener mechanism to attach callbacks, we don’t need any of the tricks above – we get the originating control handle as the callback function’s first input arg, and we can access it directly.

    Unfortunately, we cannot pass extra args to the callback that we specify using addlistener (this seems like a trivial and natural thing to have, for MathWorks’ attention…). In other words, addlistener only accepts a function handle as callback, not a cell array. To bypass this limitation in uicontrols, we typically add the extra parameters to the control’s UserData or ApplicationData properties (the latter via the setappdata function). But alas – toolstrip components have neither of these properties, nor can we add them in runtime (as with for other GUI controls). So we need to find some other way to pass these extra values, such as using global variables, or making the callback function nested so that it could access the parent function’s workspace.

Additional component properties

Component text labels, where relevant, can be set using the component’s Text property, and the tooltip can be set via the Description property. As I noted in my previous post, I believe that this is an unfortunate choice of property names. In addition, components have control-specific properties such as Value (checkboxes and toggle buttons). These properties can generally be modified in runtime, in order to reflect the program state. For example, we can disable/enable controls, and modify their label, tooltip and state depending on the control’s new state and the program state in general.

The component icon can be set via the Icon property, where available (for example, buttons have an icon, but checkboxes do not). There are several different ways in which we can set this Icon. I will discuss this in detail in the following post; in the meantime you can review the usage examples in the previous post.

There are a couple of additional hidden component properties that seem promising, most notably Shortcut and Mnemonic (the latter (Mnemonic) is also available in Section and Tab, not just in components). Unfortunately, at least as of R2018b these properties do not seem to be connected yet to any functionality. In the future, I would expect them to correspond to keyboard shortcuts and underlined mnemonic characters, as these functionalities behave in standard menu items.

Accessing the underlying Java control

As long as we’re not displaying the toolstrip on a browser page (i.e., inside a uifigure or Matlab Online), the toolstrip is basically composed of Java Swing components from the com.mathworks.toolstrip.components package (such as TSButton or TSCheckBox). I will discuss these Java classes and their customizations in a later post, but for now I just wish to show how to access the underlying Java component of any Matlab MCOS control. This can be done using a central registry of toolstrip components (so-called “widgets”), which is accessible via the ToolGroup‘s hidden ToolstripSwingService property, and then via each component’s hidden widget Id. For example:

>> widgetRegistry = hToolGroup.ToolstripSwingService.Registry;
>> jButton = widgetRegistry.getWidgetById(hButton.getId)  % get the hButton's underlying Java control
ans =
com.mathworks.toolstrip.components.TSToggleButton[,"Colorbar",layout<>,NORMAL]

We can now apply a wide variety of Java-based customizations to the retrieved jButton, as I have shown in many other articles on this website over the past decade.

Another way to access the toolstrip Java component hierarchy is via hToolGroup.Peer.get(tabIndex).getComponent. This returns the top-level Java control representing the tab whose index in tabIndex (0=left-most tab):

>> jToolGroup = hToolGroup.Peer;  % or: =hToolGroup.ToolstripSwingService.SwingToolGroup;
>> jDataTab = jToolGroup.get(0).getComponent;  % Get tab #0 (first tab: "Data")
>> jDataTab.list   % The following is abridged for brevity
com.mathworks.toolstrip.impl.ToolstripTabContentPanel[tab0069230a-52b0-4973-b025-2171cd96301b,0,0,831x93,...]
 SectionWrapper(section54fb084c-934d-4d31-9468-7e4d66cd85e5)
  com.mathworks.toolstrip.impl.ToolstripSectionComponentWithHeader[,0,0,241x92,...]
   com.mathworks.toolstrip.components.TSPanel[section54fb084c-934d-4d31-9468-7e4d66cd85e5,,layout<HORIZONTAL>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSButton[,"Refresh all",layout<>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSButton[,"Refresh X,Y",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSButton[,"Refresh Y,Z",layout<>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSButton[,"Refresh X",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSButton[,"Refresh Y",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSButton[,"Refresh Z",layout<>,NORMAL]
 SectionWrapper(sectionebd8ab95-fd33-4a3d-8f24-152589713994)
  com.mathworks.toolstrip.impl.ToolstripSectionComponentWithHeader[,0,0,159x92,...]
   com.mathworks.toolstrip.components.TSPanel[sectionebd8ab95-fd33-4a3d-8f24-152589713994,,layout<HORIZONTAL>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSCheckBox[,"Axes borders",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSCheckBox[,"Log scaling",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSCheckBox[,"Inverted Y",layout<>,NORMAL]
 SectionWrapper(section01995bfd-61de-490f-aa22-de50bae1af75)
  com.mathworks.toolstrip.impl.ToolstripSectionComponentWithHeader[,0,0,125x92,...]
   com.mathworks.toolstrip.components.TSPanel[section01995bfd-61de-490f-aa22-de50bae1af75,,layout<HORIZONTAL>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSToggleButton[,"Legend",layout<>,NORMAL]
    TSColumn -> layout<> :
     com.mathworks.toolstrip.components.TSLabel[null," ",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSToggleButton[,"Colorbar",layout<>,NORMAL]
     com.mathworks.toolstrip.components.TSLabel[null," ",layout<>,NORMAL]
 com.mathworks.mwswing.MJButton[toolstrip.header.collapseButton,808,70,20x20,...]

Toolstrip miniseries roadmap

The next post will discuss icons, for both toolstrip controls as well as the ToolGroup app window.

I plan to discuss complex components in subsequent posts. Such components include button-group, drop-down, listbox, split-button, slider, popup form, gallery etc.

Following that, my plan is 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.

Have I already mentioned that Matlab toolstrips can be a bit complex?

If you would like me to assist you in building a custom toolstrip or GUI for your Matlab program, please let me know.

Happy New Year, everyone!

]]>
https://undocumentedmatlab.com/blog/matlab-toolstrip-part-4-control-customization/feed 0
Reverting axes controls in figure toolbarhttps://undocumentedmatlab.com/blog/reverting-axes-controls-in-figure-toolbar https://undocumentedmatlab.com/blog/reverting-axes-controls-in-figure-toolbar#comments Sun, 23 Dec 2018 19:52:19 +0000 https://undocumentedmatlab.com/?p=8171
 
Related posts:
  1. uiundo – Matlab’s undocumented undo/redo manager The built-in uiundo function provides easy yet undocumented access to Matlab's powerful undo/redo functionality. This article explains its usage....
  2. Docking figures in compiled applications Figures in compiled applications cannot officially be docked since R2008a, but this can be done using a simple undocumented trick....
  3. Toolbar button labels GUI toolbar button labels can easily be set and customized using underlying Java components. ...
  4. Customizing figure toolbar background Setting the figure toolbar's background color can easily be done using just a tiny bit of Java magic powder. This article explains how. ...
 
]]>
I planned to post a new article in my toolstrip mini-series, but then I came across something that I believe has a much greater importance and impacts many more Matlab users: the change in Matlab R2018b’s figure toolbar, where the axes controls (zoom, pan, rotate etc.) were moved to be next to the axes, which remain hidden until you move your mouse over the axes. Many users have complained about this unexpected change in the user interface of such important data exploration functionality:

R2018a (standard toolbar)

R2018a (standard toolbar)

R2018b (integrated axes toolbar)

R2018b (integrated axes toolbar)

Luckily, we can revert the change, as was recently explained in this Answers thread:

addToolbarExplorationButtons(gcf) % Add the axes controls back to the figure toolbar
 
hAxes.Toolbar.Visible = 'off'; % Hide the integrated axes toolbar
%or:
hAxes.Toolbar = []; % Remove the axes toolbar data

And if you want to make these changes permanent (in other words, so that they would happen automatically whenever you open a new figure or create a new axes), then add the following code snippet to your startup.m file (in your Matlab startup folder):

try %#ok
    if ~verLessThan('matlab','9.5')
        set(groot,'defaultFigureCreateFcn',@(fig,~)addToolbarExplorationButtons(fig));
        set(groot,'defaultAxesCreateFcn',  @(ax,~)set(ax.Toolbar,'Visible','off'));
    end
end

MathWorks is taking a lot of heat over this change, and I agree that it could have done a better job of communicating the workaround in placing it as settable configurations in the Preferences panel or elsewhere. Whenever an existing functionality is broken, certainly one as critical as the basic data-exploration controls, MathWorks should take extra care to enable and communicate workarounds and settable configurations that would enable users a gradual smooth transition. Having said this, MathWorks does communicate the workaround in its release notes (I’m not sure whether this was there from the very beginning or only recently added, but it’s there now).

In my opinion the change was *not* driven by the marketing guys (as was the Desktop change from toolbars to toolstrip back in 2012 which received similar backlash, and despite the heated accusations in the above-mentioned Answers thread). Instead, I believe that this change was technically-driven, as part of MathWorks’ ongoing infrastructure changes to make Matlab increasingly web-friendly. The goal is that eventually all the figure functionality could transition to Java-script -based uifigures, replacing the current (“legacy”) Java-based figures, and enabling Matlab to work remotely, via any browser-enabled device (mobiles included), and not be tied to desktop operating systems. In this respect, toolbars do not transition well to webpages/Javascript, but the integrated axes toolbar does. Like it or not, eventually all of Matlab’s figures will become web-enabled content, and this is simply one step in this long journey. There will surely be other painful steps along the way, but hopefully MathWorks would learn a lesson from this change, and would make the transition smoother in the future.

Once you regain your composure and take the context into consideration, you might wish to let MathWorks know what you think of the toolbar redesign here. Please don’t complain to me – I’m only the messenger…

Merry Christmas everybody!

p.s. One of the complaints against the new axes toolbar is that it hurts productivity by forcing users to wait for the toolbar to fade-in and become clickable. Apparently the axes toolbar has a hidden private property called FadeGroup that presumably controls the fade-in/out effect. This can be accessed as follows:

hFadeGroup = struct(hAxes.Toolbar).FadeGroup  % hAxes is the axes handle

I have not [yet] discovered if and how this object can be customized to remove the fade animation or control its duration, but perhaps some smart hack would discover and post the workaround here (or let me know in a private message that I would then publish anonymously).

]]>
https://undocumentedmatlab.com/blog/reverting-axes-controls-in-figure-toolbar/feed 5
Matlab toolstrip – part 3 (basic customization)https://undocumentedmatlab.com/blog/matlab-toolstrip-part-3-basic-customization https://undocumentedmatlab.com/blog/matlab-toolstrip-part-3-basic-customization#comments Sun, 16 Dec 2018 16:00:29 +0000 https://undocumentedmatlab.com/?p=8077
 
Related posts:
  1. Matlab toolstrip – part 2 (ToolGroup App) Matlab users can create custom Apps with toolstrips and docked figures. ...
  2. Matlab toolstrip – part 4 (control customization) Matlab toolstrip components (controls) can be customized in various ways, including user-defined callbacks. ...
  3. Matlab toolstrip – part 6 (complex controls) Multiple types of customizable controls can be added to Matlab toolstrips...
  4. Figure window customizations Matlab figure windows can be customized in numerous manners using the underlying Java Frame reference. ...
 
]]>
In the previous post I showed how we can create custom Matlab apps. In such apps, the toolstrip is very often an important part. Today I continue my miniseries on toolstrips. Toolstrips can be a bit complex so I’m trying to proceed slowly, with each post in the miniseries building on the previous posts. So I encourage you to review the earlier posts in the miniseries (part1, part2) before reading this post.

A Matlab toolstrip is composed of a hierarchy of user-interface objects as follows (all objects are classes within the matlab.ui.internal.toolstrip package):

Anatomy of a Matlab app with toolstrip

Anatomy of a Matlab app with toolstrip

  • TabGroup
    • Tab
      • Section
        • Column
          • Component
            • Component
          • Column
        • Section
      • Tab
    • TabGroup

    In this post I explain how we can create a custom toolstrip that contains tabs, sections, and basic controls that interact with the user and the docked figures. The following posts will show more advanced customizations and more complex controls, as well as showing alternative ways of creating the toolstrip.

    1. Creating a bare toolstrip and new tabs

    We start with a new ToolGroup that has a bare toolstrip and a docked figure (for details and explanations refer to the previous post):

    % Create a new ToolGroup ("app") with a hidden DataBrowser
    hToolGroup = matlab.ui.internal.desktop.ToolGroup('Toolstrip example on UndocumentedMatlab.com');
    hToolGroup.disableDataBrowser();
    hToolGroup.open();  % this may be postponed further down for improved performance
     
    % Store toolgroup reference handle so that app will stay in memory
    jToolGroup = hToolGroup.Peer;
    internal.setJavaCustomData(jToolGroup, hToolGroup);
     
    % Create two figures and dock them into the ToolGroup
    hFig1 = figure('Name','3D');  surf(peaks);
    hToolGroup.addFigure(hFig1);

    We now create a new TabGroup and and it to our ToolGroup:

    import matlab.ui.internal.toolstrip.*  % for convenience below
    hTabGroup = TabGroup();
    hToolGroup.addTabGroup(hTabGroup);

    We can add a new Tab to the TabGroup using either of two methods:

    1. Create a new Tab object and then use TabGroup.add(hTab,index) to add it to a parent TabGroup. The index argument is optional – if specified the section is inserted at that index location; if not, it is added at the end of the tab-group. Sample usage:
      hTab = Tab('Data');
      hTabGroup.add(hTab);  % add to tab as the last section
      hTabGroup.add(hTab,3);  % add to tab as the 3rd section
    2. Call TabGroup.addTab(title). This creates a new tab with the specified title (default: ”) and adds it at the end of the tab-group. The new tab’s handle is returned by the function. Sample usage:
      hTabGroup.addTab('Data');  % add to tab-group as the last tab

    This creates an empty “Data” tab in our app toolstrip. Note that the tab title is capitalized (“DATA”), despite the fact that we set its Title property to 'Data'. Also note that while the tab’s Title property can be updated after the tab is created, in practice the tab title does not seem to change.

    New (empty) toolstrip tab

    Lastly, note that a “VIEW” tab is automatically added to our toolstrip. As explained in the previous post, we can remove it using hToolGroup.hideViewTab; (refer to the previous post for details).

    2. Adding sections to a toolstrip tab

    Each toolstrip Tab is composed of Sections, that holds the actual components. We cannot add components directly to a Tab: they have to be contained within a Section. A toolstrip Tab can only contain Sections as direct children.

    We can add a new section to a Tab using either of two methods, in a similar way to the that way we added a new tab above:

    1. Create a new Section object and then use Tab.add(hSection,index) to add it to a parent Tab. The index argument is optional – if specified the section is inserted at that index location; if not, it is added at the end of the tab. Sample usage:
      hSection = Section('Section title');
      hTab.add(hSection);  % add to tab as the last section
      hTab.add(hSection,3);  % add to tab as the 3rd section
    2. Call Tab.addSection(title). This creates a new section with the specified title (default: ”) and adds it at the end of the tab. The new section’s handle is returned by the function. Sample usage:
      hTab.addSection('Section title');  % add to tab as the last section

    Note that the help section for Tab.addSection() indicates that it’s possible to specify 2 string input args (presumably Title and Tag), but this is in fact wrong and causes a run-time error, since Section constructor only accepts a single argument (Title), at least as of R2018b.

    The Section‘s Title property can be set both in the constructor, as well as updated later. In addition, we can also set the Tag and CollapsePriority properties after the section object is created (these properties cannot be set in the constructor call):

    hSection.Title = 'New title';    % can also be set in constructor call
    hSection.Tag = 'section #1';     % cannot be set in constructor call
    hSection.CollapsePriority = 10;  % cannot be set in constructor call

    The CollapsePriority property is responsible for controlling the order in which sections and their internal components collapse into a drop-down when the window is resized to a smaller width.

    Like tabs, section titles also appear capitalized. However, unlike the section titles can indeed be modified in run-time.

    3. Adding columns to a tab section

    Each Section in a toolstrip Tab is composed of Columns, and each Column can contain 1-3 Components. This is a very effective layout for toolstrip controls that answers the vast majority of use-cases. In some special cases we might need more flexibility with the component layout within a Tab – I will explain this in a future post. But for now let’s stick to the standard Tab-Section-Column-Component framework.

    We can add columns to a section using (guess what?) either of two methods, as above:

    1. Create a new Column object and then use Section.add(hColumn,index) to add it to a parent Section. The index argument is optional – if specified the column is inserted at that index location; if not, it is added at the end of the section. Sample usage:
      hColumn = Column('HorizontalAlignment','center', 'Width',150);
      hSection.add(hColumn);  % add to section as the last column
      hSection.add(hColumn,3);  % add to section as the 3rd column
    2. Call Tab.addSection(title). This creates a new section with the specified title (default: ”) and adds it at the end of the tab. The new section’s handle is returned by the function. Sample usage:
      hSection.addColumn('HorizontalAlignment','center', 'Width',150);  % add to section as the last column

    We can set the Column‘s HorizontalAlignment and Width properties only in the constructor call, not later via direct assignments. In contrast, the Tag property cannot be set in the constructor, only via direct assignment:

    hColumn.HorizontalAlignment = 'right';  % error: can only be set via constructor call: Column('HorizontalAlignment','right', ...)
    hColumn.Width = 150;                    % error: can only be set via constructor call: Column('Width',150, ...)
    hColumn.Tag = 'column #2';              % ok: cannot be set via the constructor call!

    This is indeed confusing and non-intuitive. Perhaps this is part of the reason that the toolstrip API is still not considered stable enough for a general documented release.

    4. Adding controls to a section column

    Each section column contains 1 or more Components. These can be push/toggle/split/radio buttons, checkboxes, drop-downs, sliders, spinners, lists etc. Take a look at matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+toolstrip/ for a full list of available controls. I’ll discuss a few basic controls in this post, and more complex ones in future posts.

    As above, there are two methods for adding components to a section column, but they have different purposes:

    1. Column.addEmptyControl() adds a filler space in the next position of the column. This is used to display the colorbar control at the center of the column in the usage snippet below.
    2. Create a new Component object and then use Column.add(hComponent, index) to add it to a parent Column. The index argument is optional – if specified the component is inserted at that index location; if not, it is added at the end of the column. Sample usage:
      hButton = Button('Click me!');
      hColumn.add(hButton);  % add to column as the last component
      hColumn.add(hButton,3);  % add to column as the 3rd component

    Component objects (matlab.ui.internal.toolstrip.base.Component, which all usable toolstrip controls inherit) have several common properties. Leaving aside the more complex components for now, most usable controls include the following properties:

    • Text – text label, displayed next to the control icon (pity that MathWorks didn’t call this property String or Label, in line with uicontrols/menu-items)
    • Description – tooltip, displayed when hovering the mouse over the control (pity that MathWorks didn’t call this property Tooltip in line with uicontrols/menu-items)
    • Tag – a string, as all other Matlab HG objects. Controls are searchable by their Tag via their container’s find(tag) and findAll(tag) methods (again, I don’t quite understand why not use findobj and findall as with the rest of Matlab HG…).
    • Enabled – a logical value (true/false), true by default
    • Icon – the icon used next to the Text label. We can use the Icon constructor (that expects the full path of a PNG/JPG file), or one of its static icons (e.g. Icon.REFRESH_16). Icons will be discussed in detail in the following post; in the meantime you can see various usage examples below.

    Each control also has one or more callbacks that can be specified, as settable properties and/or as events that can be listened-to using the addlistener function. This too will be discussed in detail in the next post, but in the meantime you can see various usage examples below.

    Columns can have 1-3 components:

    • If only 1 component is specified, it is allocated the full column height, effectively creating a large control, with the Icon on top (typically a 24×24 icon) and the Text label beneath.
    • If 2 or 3 components are specified, then smaller controls are displayed, with the Text label to the right of the Icon (typically 16×16), and the controls evenly spaced within the column.
    • If you try to add more than 3 components to a Column, you’ll get a run-time error.

    5. Usage example

    Here is a short usage example showing the above concepts. The code is not pretty by any means – I intentionally wanted to display multiple different ways of adding components, specifying properties and callbacks etc. It is meant merely as an educational tool, and is not close to being ready for production code. So please don’t complain about the known fact that the code is ugly, non-robust, and in general exhibits bad programming practices. The complete runnable code can be downloaded here.

    The following code snippets assume that you have already ran the code in paragraph 1 above:

    Push-buttons section (3 columns)
    Toolstrip example (basic controls)

    Toolstrip example (basic controls)

    section1 = hTab.addSection('Push buttons');
     
    column1a = section1.addColumn();
    icon = Icon.REFRESH_24; % built-in: see Icon.showStandardIcons()
    button = Button('Refresh all',icon);
    button.Description = 'Refresh the charted data - all axes';
    button.ButtonPushedFcn = @refreshAllData;
    column1a.add(button);
    function refreshAllData(hAction,hEventData)
        hAxes = gca;
        hChildren = hAxes.Children;
        for idx = 1 : numel(hChildren)
            hChild = hChildren(idx);
            hChild.XData = -hChild.XData;
            hChild.YData = -hChild.YData;
            hChild.ZData = -hChild.ZData;
        end
    end
     
    column1b = section1.addColumn();
    addRefresh2Button('X','Y');
    addRefresh2Button('Y','Z');
    function addRefresh2Button(type1, type2)
        import matlab.ui.internal.toolstrip.*
        hButton = Button(['Refresh ' type1 ',' type2], Icon.RESTORE_16);
        hButton.Description = ['Refresh the charted data - ' type1 ',' type2 ' axes'];
        hButton.ButtonPushedFcn = {@refres2AxisData, type1, type2};
        column1b.add(hButton);
     
        function refres2AxisData(~,~,type1,type2)
            hAxes = gca;
            hChildren = hAxes.Children;
            for idx = 1 : numel(hChildren)
                hChild = hChildren(idx);
                hChild.([type1 'Data']) = -hChild.([type1 'Data']);
                hChild.([type2 'Data']) = -hChild.([type2 'Data']);
            end
        end
    end
     
    column1c = section1.addColumn();
    addRefresh1Button('X');
    addRefresh1Button('Y');
    addRefresh1Button('Z');
    function addRefresh1Button(type)
        import matlab.ui.internal.toolstrip.*
        hButton = Button(['Refresh ' type], Icon.REDO_16);
        hButton.Description = ['Refresh the charted data - ' type ' axes'];
        addlistener(hButton, 'ButtonPushed', @refres1AxisData);  % {} not supported!
        column1c.add(hButton);
     
        function refres1AxisData(h,e)
            hAxes = gca;
            hChildren = hAxes.Children;
            for idx = 1 : numel(hChildren)
                hChild = hChildren(idx);
                hChild.([type 'Data']) = -hChild.([type 'Data']);
            end
        end
    end
    Toggle buttons section (2 columns)
    section2 = hTab.addSection('Toggle buttons');
    section2.CollapsePriority = 2;
     
    column1 = Column();
    section2.add(column1);
    %icon = Icon.LEGEND_24;
    icon = Icon(fullfile(matlabroot,'toolbox','shared','controllib','general','resources','toolstrip_icons','Legend_24.png')); % PNG/JPG image file (not GIF!)
    button = ToggleButton('Legend',icon);
    button.Description = 'Toggle legend display';
    addlistener(button, 'ValueChanged', @(h,e)legend('toggle'));
    column1.add(button);
     
    column2 = section2.addColumn();
    imagefile = fullfile(matlabroot,'toolbox','matlab','icons','tool_colorbar.png');
    jIcon = javax.swing.ImageIcon(imagefile); % Java ImageIcon from file (inc. GIF)
    %jIcon = javax.swing.ImageIcon(jIcon.getImage.getScaledInstance(24,24,jIcon.getImage.SCALE_SMOOTH))  % Resize icon to 24x24
    icon = Icon(jIcon);
    button = ToggleButton('Colorbar',icon);
    button.Description = 'Toggle colorbar display';
    button.ValueChangedFcn = @toggleColorbar;
    column2.addEmptyControl();
    column2.add(button);
    column2.addEmptyControl();
    function toggleColorbar(hAction,hEventData)
        if hAction.Selected
            colorbar;
        else
            colorbar('off');
        end
    end
    Checkboxes section (1 column 150px-wide), placed after the push-buttons section
    section3 = Section('Checkboxes');
    section3.CollapsePriority = 1;
    hTab.add(section3, 2);
     
    column3 = section3.addColumn('HorizontalAlignment','left', 'Width',150);
     
    button = CheckBox('Axes borders', true);
    button.ValueChangedFcn = @toggleAxes;
    button.Description = 'Axes borders';
    column3.add(button);
    function toggleAxes(hAction,hEventData)
        if hAction.Selected
            set(gca,'Visible','on');
        else
            set(gca,'Visible','off');
        end
    end
     
    button = CheckBox('Log scaling', false);
    button.addlistener('ValueChanged',@toggleLogY);
    button.Description = 'Log scaling';
    column3.add(button);
    function toggleLogY(hCheckbox,hEventData)
        if hCheckbox.Value, type = 'log'; else, type = 'linear'; end
        set(gca, 'XScale',type, 'YScale',type, 'ZScale',type);
    end
     
    button = CheckBox('Inverted Y', false);
    button.addlistener('ValueChanged',@toggleInvY);
    button.Description = 'Invert Y axis';
    column3.add(button);
    function toggleInvY(hCheckbox,~)
        if hCheckbox.Value, type = 'reverse'; else, type = 'normal'; end
        set(gca, 'YDir',type);
    end

    Summary

    Creating a custom app toolstrip requires careful planning of the tabs, sections, controls and their layout, as well as preparation of the icons, labels and callbacks. Once you start playing with the toolstrip API, you’ll see that it’s quite easy to understand and to use. I think MathWorks did a good job in general with this API, and it’s a pity that they did not make it public or official long ago (the MCOS API discussed above existed since 2014-2015; earlier versions existed at least as far back as 2011). Comparing the changes made in the API between R2018a and R2018b shows quite minor differences, which may possibly means that the API is now considered stable, and therefore that it might well be made public in some near-term future. Still, note that this API may well change in future releases (for example, naming of the control properties that I mentioned above). It works well in R2018b, as well as in the past several Matlab releases, but this could well change in the future, so beware.

    In the following posts I will discuss advanced control customizations (icons, callbacks, collapsibility etc.), complex controls (drop-downs, pop-ups, lists, button groups, items gallery etc.) and low-level toolstrip creation and customization. As I said above, Matlab toolstrips are quite an extensive subject and so I plan to proceed slowly, with each post building on its predecessors. Stay tuned!

    In the meantime, if you would like me to assist you in building a custom toolstrip or GUI for your Matlab program, please let me know.

    ]]>
    https://undocumentedmatlab.com/blog/matlab-toolstrip-part-3-basic-customization/feed 17
    Matlab toolstrip – part 2 (ToolGroup App)https://undocumentedmatlab.com/blog/matlab-toolstrip-part-2-toolgroup-app https://undocumentedmatlab.com/blog/matlab-toolstrip-part-2-toolgroup-app#comments Wed, 05 Dec 2018 17:00:48 +0000 https://undocumentedmatlab.com/?p=8059
     
    Related posts:
    1. Figure window customizations Matlab figure windows can be customized in numerous manners using the underlying Java Frame reference. ...
    2. Builtin PopupPanel widget We can use a built-in Matlab popup-panel widget control to display lightweight popups that are attached to a figure window. ...
    3. Toolbar button labels GUI toolbar button labels can easily be set and customized using underlying Java components. ...
    4. Matlab toolstrip – part 3 (basic customization) Matlab toolstrips can be created and customized in a variety of ways. ...
     
    ]]>
    A while ago I posted the first of my planned miniseries on the Matlab toolstrip (ribbon). Today I will expand that post by discussing how toolstrips can be added to Matlab GUIs. This post will remain at a high-level as the previous post, with followup posts drilling into the technical details of the toolstrip components (inner packages and classes).

    We can add a Matlab toolstrip to 3 types of Matlab GUI windows:

    1. To a Java-based Matlab figure (so-called “legacy” figures, created using GUIDE or the figure function)
    2. To a container window of docked Java-based figures, typically called an “App” (marketing name) or “Tool Group” (internal technical name)
    3. To a JavaScript/HTML-based Matlab figure (so called “web” figures, created using App Designer or the uifigure function)

    Today I will show how to add a basic dynamic toolstrip to a ToolGroup (App, window type #2):

    ToolGroup with clients and dynamic toolstrip

    ToolGroup with clients and dynamic toolstrip


    Figure containers (“Tool Groups”)

    Most Matlab users are familiar with window types #1 and #3 (legacy and web-based figures), but type #2 may seem strange. In fact, it shouldn’t be: All the Matlab “Apps” and Desktop components use such a container of docked clients. For example, both the Matlab Editor and Desktop are containers of individual client windows (individual files in the Editor; Command Window, Workspace etc. in the desktop).

    Similarly, when we dock figures, they dock as client windows into a container called “Figures” (this can be controlled programmatically: see my setFigDockGroup utility on the File Exchange). This is the basis for all Matlab “Apps”, as far as I am aware (some Apps may possibly use a different GUI container, after all there are ~100 Matlab Apps and I’m not familiar with all of them). Such Apps are basically stand-alone Tool Groups (client container windows) that contain one or more docked figures, a toolstrip, and a side-panel with controls (so-called “Data Browser”).

    Note: MathWorks uses confusing terminology here, using the same term “App” for both MathWorks-created GUIs containers (that have toolstrips, Data Browser and docked figures) and also user-created utilities on the File Exchange (that do not have these). Unfortunately, MathWorks has chosen not [yet] to release to the general public its set of tools that enable creating true “Apps”, i.e. those that have a toolstrip, Data Browser and docked figures.

    Today’s post will attempt to fill this gap, by showing how we can create user Apps that have a toolstrip and docked figures. I will ignore the Data Browser today, and will describe it in a future post. Since docking figures into a standalone user-created container is a solved problem (using my setFigDockGroup utility), this post will focus on adding a toolstrip to such a container.

    A ToolGroup object (matlab.ui.internal.desktop.ToolGroup) is created either implicitly (by docking a figure into a group that has a new name), or explicitly (by invoking its constructor):

    % Create a new non-visible empty App (Tool Group)
    hToolGroup = matlab.ui.internal.desktop.ToolGroup('Toolstrip example on UndocumentedMatlab.com');

    Some things only work properly after the app is displayed, so let’s display the ToolGroup (however, note that for improved performance it is better to do whatever customizations and GUI updates that you can before the app is made visible):

    % Display the ToolGroup window
    hToolGroup.open();

    Basic empty ToolGroup (without toolstrip or clients)

    Basic empty ToolGroup (without toolstrip or clients)

    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. There are many ways to persist this variable. Here’s one alternative, in which we persist it in itself (or rather, attached to its internal Java peer control):

    % Store toolgroup reference handle so that app will stay in memory
    jToolGroup = hToolGroup.Peer;
    internal.setJavaCustomData(jToolGroup, hToolGroup);

    internal.setJavaCustomData is an undocumented Matlab function that adds a new custom property to a Java reference handle. In our case, it adds a CustomData property to the jToolGroup handle and sets its value to the Matlab hToolGroup handle. The source code for internal.setJavaCustomData is available in %matlabroot%/toolbox/shared/controllib/general/+internal/setJavaCustomData.m and is very simple: it essentially uses the old schema-based schema.prop method for adding new properties to handles. Schema is an old deprecated mechanism that is mostly replaced by the newer MCOS (Matlab Class Object System), but for some specific cases such as this it’s still very useful (the standard addprop function can add new properties to Matlab GUI handles, but not to Java reference handles).

    Finally, let’s discard the Data Browser side panel (I’ll discuss it in a separate future post):

    % Discard the Data-browser left panel
    hToolGroup.disableDataBrowser();

    Adding a toolstrip to the ToolGroup

    Now that we have the basic container ready, let’s add a toolstrip. To simplify matters in this introductory post (after all, I have still not described the internal packages and classes that make up a toolstrip), we’ll use some of the tabs used in the showcaseToolGroup example that I discussed in my previous post. You can see the relevant source code in %matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+desktop/*.m, in case you want to jump ahead and customize your own toolstrip tabs, groups and buttons. In the code snippet below, we first create an empty TabGroup, then add toolstrip tabs into it, and finally add this TabGroup into our ToolGroup using its addTabGroup(hTabGroup) method:

    % Create a new tab group
    %hTabGroup = matlab.ui.internal.desktop.showcaseBuildTabGroup('swing');
    hTabGroup = matlab.ui.internal.toolstrip.TabGroup();
    hTab1 = matlab.ui.internal.desktop.showcaseBuildTab_Buttons('swing');
    hTabGroup.add(hTab1);
    %hTabGroup.add(matlab.ui.internal.desktop.showcaseBuildTab_Gallery());
    hTabGroup.add(matlab.ui.internal.desktop.showcaseBuildTab_Layout('swing'));
     
    % Select tab #1 (common)
    hTabGroup.SelectedTab = hTab1;
     
    % Add the tab group to the built-in toolstrip
    hToolGroup.addTabGroup(hTabGroup);

    We now have an “App” that has a toolstrip, but no clients (yet), and a hidden Data Browser side-panel:

    ToolGroup "App" with a simple toolstrip (no clients yet)

    Now let’s add some clients (docked figures):

    Adding clients (docked figures) to the ToolGroup

    There are two easy variants for adding docked figures in a ToolGroup: The easiest is to use the ToolGroup’s addFigure(hFigure) method:

    % Create a figure and dock it into the tool-group
    hFig1 = figure('Name','3D');
    surf(peaks);
    hToolGroup.addFigure(hFig1);

    The second variant enables to dock a figure that has a specific set of toolstrip tabs attached to it. These tabs will only display in the toolstrip when that particular figure has focus. We do this by creating a new TabGroup (just as we have done above), and then add the figure and TabGroup to the container using the ToolGroup’s addClientTabGroup(hFigure,hTabGroup) method:

    % Create the 2nd figure
    hFig2 = figure('Name','2D');
    plot(rand(5)); drawnow
     
    % Add a few tabs that are only relevant to this specific figure
    hTabGroup2 = matlab.ui.internal.toolstrip.TabGroup();
    hTab2 = matlab.ui.internal.desktop.showcaseBuildTab_Selections();
    hTabGroup2.add(hTab2);
    hTabGroup2.add(matlab.ui.internal.desktop.showcaseBuildTab_EditValue());
     
    % Add the figure and tabs to the ToolGroup
    hToolGroup.addClientTabGroup(hFig2, hTabGroup2);

    ToolGroup with clients and dynamic toolstrip

    ToolGroup with clients and dynamic toolstrip

    In this example, the “Selection” and “Values” toolstrip tabs only appear when the 2nd figure (“2D”) has focus. A similar behavior exists in the Matlab Desktop and Editor, where some tabs are only shown when certain clients have focus.

    Removing the View tab

    Note that the “View” toolstrip tab (which enables setting the appearance of the docked figures: layout, tab positions (top/bottom/left/right), ordering etc.) is automatically added to the toolstrip and always appears last. We can remove this View tab using the ToolGroup’s hideViewTab() method. The tab will not immediately be removed, only when the toolstrip is repainted, for example, when we switch focus between the docked figures:

    hToolGroup.hideViewTab;  % toolstrip View tab is still visible at this point
    figure(hFig1);  % change focus to hFig1 - toolstrip is repainted without View tab

    Conclusion

    It’s relatively easy to dock figures into a standalone “App” window that has a custom toolstrip, which can even be dynamically modified based on the figure which is currently in focus. Naturally, this has little benefit if we cannot customize the toolstrip components: labels, icons, control type, grouping and most importantly – callbacks. This topic deserves a dedicated post, which I plan to be the next in this miniseries. Stay tuned – hopefully the next post will not take me as long to publish as this post (I was quite busy recently)…

    ]]>
    https://undocumentedmatlab.com/blog/matlab-toolstrip-part-2-toolgroup-app/feed 7
    Matlab toolstrip – part 1https://undocumentedmatlab.com/blog/matlab-toolstrip-part-1 https://undocumentedmatlab.com/blog/matlab-toolstrip-part-1#comments Mon, 03 Sep 2018 15:00:22 +0000 https://undocumentedmatlab.com/?p=7952
     
    Related posts:
    1. Setting status-bar text The Matlab desktop and figure windows have a usable statusbar which can only be set using undocumented methods. This post shows how to set the status-bar text....
    2. Modifying Matlab’s Look-and-Feel Matlab's entire Look-and-Feel (PLAF, or L&F) can be modified at the control or application level - this article shows how...
    3. Variables Editor scrolling The Matlab Variables Editor can be accessed to provide immediate scrolling to a specified cell location. ...
    4. Programmatic shortcuts manipulation – part 1 Matlab Desktop shortcuts can be programmatically accessed and customized. ...
     
    ]]>
    The Matlab toolstrip (ribbon) has been around officially since R2012a, and unofficially for a couple of years earlier. Since then, I blogged about the toolstrip only rarely (example). I believe the time has come to start a short mini-series about this functionality, eventually showing how users can use toolstrips in their own custom applications.

    My plan is to start the miniseries with a discussion of the built-in showcase examples, followed by a post on the built-in classes that make up the toolstrip building-blocks. Finally, I’ll describe how toolstrips can be added to figures, not just in client/tool groups.

    Matlab’s internal showcase examples

    I start the discussion with a description of built-in examples for the toolstrip functionality, located in %matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+desktop/. The most important of these are showcaseToolGroup.m and showcaseMPCDesigner.m, both of which use Java-based (Swing) containers and controls. Readers who wish to integrate toolstrips into their app immediately, without waiting for my followup posts in this series, are welcome to dig into the examples’ source-code and replicate it in their programs:

    1. showcaseToolGroup
    h = matlab.ui.internal.desktop.showcaseToolGroup

    showcaseToolGroup built-in example

    2. showcaseMPCDesigner
    >> h = matlab.ui.internal.desktop.showcaseMPCDesigner
     
    h =
      showcaseMPCDesigner with properties:
     
        ToolGroup: [1×1 matlab.ui.internal.desktop.ToolGroup]
           Dialog: [1×1 toolpack.component.TSTearOffPopup]
          Figure1: [1×1 Figure]
          Figure2: [1×1 Figure]

    showcaseMPCDesigner built-in example

    3. showcaseHTML and showcaseCEF

    In addition to these showcase examples, the folder also contains a showcaseHTML.m and showcaseCEF.m files, that are supposed to showcase the toolstrip functionality in JavaScript-based containers (browser webpage and uifigure apps, respectively). Unfortunately, on my system running these classes displays blank, although the toolstrip is indeed created, as seen below (if you find out how to make these classes work, please let me know):

    >> h = matlab.ui.internal.desktop.showcaseHTML
    building toolstrip hierarchy...
    rendering toolstrip...
     
    h = 
      Toolstrip with properties:
     
                   SelectedTab: [1×1 matlab.ui.internal.toolstrip.Tab]
                  DisplayState: 'expanded'
        DisplayStateChangedFcn: @PropertyChangedCallback
                           Tag: 'toolstrip'
     
    >> hs = struct(h)
    Warning: Calling STRUCT on an object prevents the object from hiding its implementation details and should thus be avoided.
    Use DISP or DISPLAY to see the visible public details of an object. See 'help struct' for more information.
    (Type "warning off MATLAB:structOnObject" to suppress this warning.)
     
    hs = 
      struct with fields:
     
                             SelectedTab: [1×1 matlab.ui.internal.toolstrip.Tab]
                            DisplayState: 'expanded'
                  DisplayStateChangedFcn: @PropertyChangedCallback
                     DisplayStatePrivate: 'expanded'
                            QABIdPrivate: '2741bf89'
                   QuickAccessBarPrivate: [1×1 matlab.ui.internal.toolstrip.impl.QuickAccessBar]
           DisplayStateChangedFcnPrivate: @PropertyChangedCallback
             SelectedTabChangedListeners: [1×1 event.listener]
                                     Tag: 'toolstrip'
                                    Type: 'Toolstrip'
                              TagPrivate: 'toolstrip'
        WidgetPropertyMap_FromMCOSToPeer: [3×1 containers.Map]
        WidgetPropertyMap_FromPeerToMCOS: [3×1 containers.Map]
                                  Parent: [0×0 matlab.ui.internal.toolstrip.base.Node]
                                Children: [1×1 matlab.ui.internal.toolstrip.TabGroup]
                                 Parent_: []
                               Children_: [1×1 matlab.ui.internal.toolstrip.TabGroup]
                                    Peer: [1×1 com.mathworks.peermodel.impl.PeerNodeImpl]
                       PropertySetSource: [1 java.util.HashMap]
                        PeerModelChannel: '/ToolstripShowcaseChannel'
                       PeerEventListener: [1×1 handle.listener]
                     PropertySetListener: [1×1 handle.listener]
     
    >> hs.Peer
    ans =
    PeerNodeImpl{id='4a1e4b08', type='Toolstrip', properties={displayState=expanded, hostId=ToolStripShowcaseDIV, tag=toolstrip, QABId=2741bf89}, parent=878b0e2b, children=[
        PeerNodeImpl{id='5bb9632c', type='TabGroup', properties={QAGroupId=ea9b628c, tag=, selectedTab=f90db10c}, parent=4a1e4b08, children=[
            PeerNodeImpl{id='f90db10c', type='Tab', properties={mnemonic=, tag=tab_buttons, title=BUTTONS}, parent=5bb9632c, children=[
                PeerNodeImpl{id='1ccc9246', type='Section', properties={collapsePriority=0.0, mnemonic=, tag=sec_push, title=PUSH BUTTON}, parent=f90db10c, children=[
                    PeerNodeImpl{id='8323f06e', type='Column', properties={horizontalAlignment=left, width=0.0, tag=}, parent=1ccc9246, children=[
                        PeerNodeImpl{id='af368d7b', type='PushButton', properties={textOverride=, descriptionOverride=, mnemonic=, actionId=230d471b, iconOverride=, tag=pushV, iconPathOverride=}, parent=8323f06e, children=[]}]}
                    PeerNodeImpl{id='a557a712', type='Column', properties={horizontalAlignment=left, width=0.0, tag=}, parent=1ccc9246, children=[
                        PeerNodeImpl{id='f0d6a9fc', type='EmptyControl', properties={tag=}, parent=a557a712, children=[]}
                        PeerNodeImpl{id='74bc4cd2', type='PushButton', properties={textOverride=, descriptionOverride=, mnemonic=, actionId=12d6a26a, iconOverride=, tag=pushH, iconPathOverride=}, parent=a557a712, children=[]}
                        PeerNodeImpl{id='bcb5a9d0', type='EmptyControl', properties={tag=}, parent=a557a712, children=[]}]}]}
                PeerNodeImpl{id='0e515319', type='Section', properties={collapsePriority=0.0, mnemonic=, tag=sec_dropdown, title=DROP DOWN BUTTON}, parent=f90db10c, children=[
                    PeerNodeImpl{id='80482225', type='Column', properties={horizontalAlignment=left, width=0.0, tag=}, parent=0e515319, children=[
                        PeerNodeImpl{id='469f469a', type='DropDownButton', properties={textOverride=, descriptionOverride=, mnemonic=, actionId=c6ca7335, iconOverride=, tag=dropdownV, iconPathOverride=}, parent=80482225, children=[]}]}
                    ...

    Note: showcaseCEF has been removed in 2018, but is available in older Matlab releases.

    Levels of toolstrip encapsulation

    Matlab currently has several levels of encapsulation for toolstrip components:

    • Top-level m-file classes for showcasing the toolstrip functionality and creating toolstrips in Java-based containers and web-based apps – these are located in %matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+desktop/
    • Mid-level m-file classes that contain the toolstrip building blocks (tabs, sections, controls) – these are located in %matlabroot%/toolbox/matlab/toolstrip/+matlab/+ui/+internal/+toolstrip/
    • Low-level Java classes that implement the underlying user-interface for Java-based UI – these are located in %matlabroot%/java/jar/toolstrip.jar. I discussed this briefly in a post few years ago.

    The top- and mid-level m-file classes are provided with full source code that is quite well-documented internally (as m-file source-code comments). However, note that it is not officially documented or supported (i.e., semi-documented in this blog’s parlance).

    The low-level Java classes on the other hand are compiled without available source code – we can inspect these classes (e.g., using uiinspect or checkClass), but we cannot see their original source-code. Luckily, the higher-level m-file classes provide us with plenty of hints and usage examples that we could use to tailor the appearance, functionality and integration of toolstrip components into our app.

    Robyn Jackey’s Widgets Toolbox

    Users who hesitate to mess around with the built-in toolstrip functionality may find interest in MathWorker Robyn Jackey’s Toolstrip look-alike, which is part of his open-source Widgets Toolbox on the Matlab File Exchange. Unlike other parts of Robyn’s toolbox, which use undocumented functionality, his Toolstrip class seems to use documented components (panels, uicontrols etc.), with just a small reliance on undocumented functionality (matlab.ui.* for example). This has a fair chance to continue working well into future releases, even if Matlab’s built-in toolstrip functionality changes:

    Robyn Jackey's Toolstrip look-alike

    Strong caution

    Over the years, Matlab’s internal toolstrip interface has changed somewhat, but not dramatically. This could change at any time, since the toolstrip uses deeply undocumented functionality. What I will demonstrate over the next few posts might stop working in R2019a, or in R2025b – nobody really knows, perhaps not even MathWorks at this stage. Something that we do know for a fact is that Matlab is slowly transitioning away from Java-based user interfaces to web-based (HTML/JavaScript/CSS) interfaces, and this could have a drastic effect on the toolstrip functionality/API. It seems reasonable to assume that even if MathWorks would one day open up the toolstrip functionality, this would only be for the new web-based uifigure apps (not legacy Java-based figures), and might well have a different API than the one that I’ll discuss in this miniseries. Still, users could use the unofficial/undocumented information that I present here in their own Java figures today and quite possibly also in near-term upcoming releases.

    Despite the many unknowns regarding future supportability/roadmap of the built-in toolstrip API, I believe that my readers are smart enough to decide for themselves whether they want to take the associated risks to improve their Matlab programs today, or wait until a documented API will possibly be provided sometime in the future. The choice is yours, as it always is when using undocumented tips from my blog.

    With this warning stated, let’s start having fun with Matlab’s built-in toolstrip!

    ]]>
    https://undocumentedmatlab.com/blog/matlab-toolstrip-part-1/feed 2
    Matlab callbacks for uifigure JavaScript eventshttps://undocumentedmatlab.com/blog/matlab-callbacks-for-uifigure-javascript-events https://undocumentedmatlab.com/blog/matlab-callbacks-for-uifigure-javascript-events#comments Wed, 15 Aug 2018 15:00:10 +0000 https://undocumentedmatlab.com/?p=7913
     
    Related posts:
    1. Customizing uifigures part 2 Matlab's new web-based uifigures can be customized using custom CSS and Javascript code. ...
    2. Customizing uifigures part 3 As I have repeatedly posted in recent years, Matlab is advancing towards web-based GUI. The basic underlying technology is more-or-less stable: an HTML/Javascript webpage that is created-on-the-fly and rendered in...
    3. Frameless (undecorated) figure windows Matlab figure windows can be made undecorated (borderless, title-less). ...
    4. Graphic sizing in Matlab R2015b Matlab release R2015b's new "DPI-aware" nature broke some important functionality. Here's what can be done... ...
     
    ]]>
    I would like to welcome back guest blogger Iliya Romm of Israel’s Technion Turbomachinery and Heat Transfer Laboratory. Today Iliya will discuss how to assign Matlab callbacks to JavaScript events in the new web-based uifigures. Other posts on customizations of web-based Matlab GUI can be found here.

    On several occasions (including the previous post by Khris Griffis) I came across people who were really missing the ability to have Matlab respond to various JavaScript (JS) events. While MathWorks are working on their plans to incorporate something similar to this in future releases, we’ll explore the internal tools already available, in the hopes of finding a suitable intermediate solution.

    Today I’d like to share a technique I’ve been experimenting with, allowing Matlab to respond to pretty much any JS event to which we can attach a listener. This is an overview of how it works:

    1. create a UIFigure with the desired contents, and add to it (at least) one more dummy control, which has an associated Matlab callback.
    2. execute a JS snippet that programmatically interacts with the dummy control, whenever some event-of-interest happens, causing the Matlab callback to fire.
    3. query the webWindow, from within the Matlab callback, to retrieve any additional information (“payload”) that the JS passed.

    This approach allows, for example, to easily respond to mouse events:

    Attaching Matlab callback to a uifigure JavaScript event

    Consider the code below, which demonstrates different ways of responding to JS events. To run it, save the .m file function below (direct download link) and the four accompanying .js files in the same folder, then run jsEventDemo(demoNum), where demoNum is 1..4. Note: this code was developed on R2018a, unfortunately I cannot guarantee it works on other releases.

    function varargout = jsEventDemo(demoNum)
       % Handle inputs and outputs
       if ~nargin
          demoNum = 4;
       end
       if ~nargout
          varargout = {};
       end
     
       % Create a simple figure:
       hFig = uifigure('Position',[680,680,330,240],'Resize','off');
       hTA = uitextarea(hFig, 'Value', 'Psst... Come here...!','Editable','off');
       [hWin,idTA] = mlapptools.getWebElements(hTA);
     
       % Open in browser (DEBUG):
       % mlapptools.waitForFigureReady(hFig); mlapptools.unlockUIFig(hFig); pause(1);
       % web(hWin.URL,'-browser')
     
       % Prepare the JS command corresponding to the requested demo (1-4)
       switch demoNum
          % Demo #1: Respond to mouse events, inside JS, using "onSomething" bindings:
          case 1
             % Example from: https://dojotoolkit.org/documentation/tutorials/1.10/events/#dom-events
             jsCommand = sprintf(fileread('jsDemo1.js'), idTA.ID_val);
     
          % Demo #2: Respond to mouse click events, inside JS, using pub/sub:
          case 2
             % Example from: https://dojotoolkit.org/documentation/tutorials/1.10/events/#publish-subscribe
             hTA.Value = 'Click here and see what happens';
             jsCommand = sprintf(fileread('jsDemo2.js'), idTA.ID_val);
     
          % Demo #3: Trigger Matlab callbacks programmatically from JS by "pressing" a fake button:
          case 3
             hB = uibutton(hFig, 'Visible', 'off', 'Position', [0 0 0 0], ...
                           'ButtonPushedFcn', @fakeButtonCallback);
             [~,idB] = mlapptools.getWebElements(hB);
             jsCommand = sprintf(fileread('jsDemo3.js'), idTA.ID_val, idB.ID_val);
     
          % Demo 4: Trigger Matlab callbacks and include a "payload" (i.e. eventData) JSON:
          case 4
             hB = uibutton(hFig, 'Visible', 'off', 'Position', [0 0 0 0],...
                          'ButtonPushedFcn', @(varargin)smartFakeCallback(varargin{:}, hWin));
             [~,idB] = mlapptools.getWebElements(hB);
             jsCommand = sprintf(fileread('jsDemo4.js'), idTA.ID_val, idB.ID_val);
       end % switch
     
       % Execute the JS command
       hWin.executeJS(jsCommand);
    end
     
    % Matlab callback function used by Demo #3
    function fakeButtonCallback(obj, eventData) %#ok<INUSD>
       disp('Callback activated!');
       pause(2);
    end
     
    % Matlab callback function used by Demo #4
    function smartFakeCallback(obj, eventData, hWin)
       % Retrieve and decode payload JSON:
       payload = jsondecode(hWin.executeJS('payload'));
     
       % Print payload summary to the command window:
       disp(['Responding to the fake ' eventData.EventName ...
             ' event with the payload: ' jsonencode(payload) '.']);
     
       % Update the TextArea
       switch char(payload.action)
          case 'enter',  act_txt = 'entered';
          case 'leave',  act_txt = 'left';
       end
       str = ["Mouse " + act_txt + ' from: '; ...
              "(" + payload.coord(1) + ',' + payload.coord(2) + ')'];  
       obj.Parent.Children(2).Value = str;
    end

    Several thoughts:

    • The attached .js files will not work by themselves, rather, they require sprintf to replace the %s with valid widget IDs. Of course, these could be made into proper JS functions.
    • Instead of loading the JS files using fileread, we could place the JS code directly in the jsCommand variable, as a Matlab string (char array)
    • I tried getting it to work with a textarea control, so that we would get the payload right in the callback’s eventData object in Matlab, Unfortunately, I couldn’t get it to fire programmatically (solutions like this didn’t work). So instead, I store the payload as JSON, and retrieve it with jsondecode(hWin.executeJS('payload')) in the smartFakeCallback function.

    JavaScript files

    1. jsDemo1.js (direct download link):
      require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse"],
      	function(on, dom, domStyle, mouse) {
      		var myDiv = dom.byId("%s");
      		on(myDiv, mouse.enter, function(evt){
      			domStyle.set(myDiv, "backgroundColor", "red");
      		});
      		on(myDiv, mouse.leave, function(evt){
      			domStyle.set(myDiv, "backgroundColor", "");
      		});
      	});
    2. jsDemo2.js (direct download link):
      require(["dojo/on", "dojo/topic", "dojo/dom"],
      	function(on, topic, dom) {
      		var myDiv = dom.byId("%s");
      		on(myDiv, "click", function() {
      			topic.publish("alertUser", "Your click was converted into an alert!");
      		});
      		topic.subscribe("alertUser", function(text){
      			alert(text);
      		});
      	});
    3. jsDemo3.js (direct download link):
      require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/mouse"],
      	function(on, dom, domStyle, mouse) {
      		var myDiv = dom.byId("%s");
      		var fakeButton = dom.byId("%s");
      		on(myDiv, mouse.enter, function(evt){
      			fakeButton.click();
      		});
      	});
    4. jsDemo4.js (direct download link):
      var payload = [];
      require(["dojo/on", "dojo/dom", "dojo/topic", "dojo/mouse"],
      	function(on, dom, topic, mouse) {
      		var myDiv = dom.byId("%s");
      		var fakeButton = dom.byId("%s");
      		topic.subscribe("sendToMatlab", function(data){
      			payload = data;
      			fakeButton.click();
      		});
      		on(myDiv, mouse.enter, function(evt){
      			data = {action: "enter",
      				coord: [evt.clientX, evt.clientY]};
      			topic.publish("sendToMatlab", data);
      		});
      		on(myDiv, mouse.leave, function(evt){
      			data = {action: "leave",
      				coord: [evt.clientX, evt.clientY]};
      			topic.publish("sendToMatlab", data);
      		});
      	});

    Conclusions

    As you can see, this opens some interesting possibilities, and I encourage you to experiment with them yourself! This feature will likely be added to the mlapptools toolbox as soon as an intuitive API is conceived.

    If you have any comments or questions about the code above, or just want to tell me how you harnessed this mechanism to upgrade your uifigure (I would love to hear about it!), feel free to leave a message below the gist on which this post is based (this way I get notifications!).

    ]]>
    https://undocumentedmatlab.com/blog/matlab-callbacks-for-uifigure-javascript-events/feed 3
    Customizing web-GUI uipanelhttps://undocumentedmatlab.com/blog/customizing-web-gui-uipanel https://undocumentedmatlab.com/blog/customizing-web-gui-uipanel#respond Wed, 01 Aug 2018 18:00:13 +0000 https://undocumentedmatlab.com/?p=7804
     
    Related posts:
    1. AppDesigner’s mlapp file format MLAPP files created by AppDesigner can be inspected and manipulated outside AppDesigner. ...
    2. Customizing uifigures part 2 Matlab's new web-based uifigures can be customized using custom CSS and Javascript code. ...
    3. Customizing uifigures part 3 As I have repeatedly posted in recent years, Matlab is advancing towards web-based GUI. The basic underlying technology is more-or-less stable: an HTML/Javascript webpage that is created-on-the-fly and rendered in...
    4. Matlab callbacks for uifigure JavaScript events Matlab callback code can be attached to JavaScript events in web-based uifigures. ...
     
    ]]>
    I would like to introduce guest blogger Khris Griffis. Today, Khris will continue the series of posts on web-based uifigure customization with an article showing how to create scrollable/customizable panels in web-based uifigures. This post follows last-week’s article, about placing controls/axes within a scroll-panel in non-web (Java-based) figures. Users interested in advanced aspects and insights on the development roadmap of web-based Matlab GUI should also read Loren Shure’s blog post from last week.

    Motivation

    As a retinal physiologist, I spend a lot of time in Matlab creating GUIs to visualize and analyze electrophysiological data. The data often requires a lot of processing and quality control checks before it can be used for interpretation and publication. Consequently, I end up with many control elements taking up precious space on my GUI.

    In Java-based (legacy/GUIDE) figures, this wasn’t a huge problem because, depending on what GUI components I needed, I could use a pure Matlab approach (a child panel within a parent panel, with a couple of control sliders moving the child panel around), or a number of Java approaches (which are always more fun; Yair described such an approach last week).

    Unfortunately, the web-based (App-Designer) figure framework doesn’t support Java, and the pure/documented Matlab approach just doesn’t look good or function very well:

    AppDesigner uislider is not a good scrollbar, no matter what we do to it!
    AppDesigner uislider is not a good scrollbar, no matter what we do to it!

    Fortunately, the new web framework allows us to use HTML, CSS and JavaScript (JS) to modify elements in the uifigure, i.e. its web-page DOM. It turns out that the uipanel object is essentially just a <div> with a Matlab-designed CSS style. We can customize it with little effort.

    The main goal here is to create a scrollable customizable uipanel containing many uicontrol elements, which could look something like this:

    Running app demo

    A simple command-window example

    Since we are building on the series of uifigure customizations, I expect you have already read the preceding related Undocumented Matlab posts:

    1. Customizing uifigures part 1
    2. Customizing uifigures part 2
    3. Customizing uifigures part 3

    Also, I highly recommend cloning (or at least downloading) the mlapptools toolbox repo on Github (thanks Iliya Romm et al.). We will use it to simplify life today.

    Using the mlapptools toolbox, we need just a few lines of code to set up a scrollable panel. The important thing is knowing how big the panel needs to be to hold all of our control objects. Once we know this, we simply set the panel’s Position property accordingly. Then we can use simple CSS to display scrollbars and define the viewing dimensions.

    For example, if we need 260px in width by 720px in height to hold our control widgets, but only have 200px height available, we can solve this problem in 3 steps:

    1. Set the uipanel‘s Dimension property to be 260px wide by 720px tall.
    2. Set the viewing height using mlapptools.setStyle(scrollPane,'height','200px') for the panel’s CSS height style attribute.
    3. Display the vertical scrollbar by calling mlapptools.setStyle(scrollPane,'overflow-y','scroll') for the panel’s CSS overflow-y='scroll' style attribute.
    % Create the figure
    fig = uifigure('Name', 'Scroll Panel Test');
     
    % Place a uipanel on the figure, color it for fun
    scrollPane = uipanel(fig, 'BackgroundColor', [0.5,0.4,1]);
     
    % Define the space requirements for the controls
    totalWidth  = 260; %px
    totalHeight = 720; %px
    viewHeight  = 200; %px
     
    % STEP 1: set the uipanel's Position property to the required dimensions
    scrollPane.Position(3) = totalWidth;  % WIDTH
    scrollPane.Position(4) = totalHeight; % HEIGHT
     
    % STEP 2: set the viewing height
    mlapptools.setStyle(scrollPane, 'height', sprintf('%dpx', viewHeight));
     
    % STEP 3: display the vertical scrollbar
    mlapptools.setStyle(scrollPane, 'overflow-y', 'scroll');
     
    % Add control elements into the uipanel
    ...

    Example scrollable uipanel in a Matlab uifigure
    Example scrollable uipanel in a Matlab uifigure

    Because this is a web-based GUI, notice that you can simply hover your mouse over the panel and scroll with your scroll wheel. Awesome, right?

    Note that the CSS height/width style attributes don’t affect the actual size of our panel, just how much of the panel we can see at once (“viewport”).

    The CSS overflow style attribute has a number of options. For example, setStyle(scrollPane,'overflow','auto') causes scrollbars to automatically hide when the viewing area is larger than panel’s dimensions. Review the CSS overflow attribute to learn about other related settings that affect the panel’s behavior.

    Styling the scrollbars

    The mlapptools toolbox utilizes dojo.js to query the DOM and set styles, which is essentially setting inline styles on the DOM element, i.e. <div ... style="color: red;background:#FEFEFE;..."> ... </div>. This has the added benefit of overriding any CSS classes Matlab is imposing on the DOM (see CSS precedence). We can inject our own classes into the page’s <head> tag and then use dojo.setClass() to apply our classes to specific GUI elements. For example, we can style our bland scrollbars by using CSS to define a custom scrollpane style class:

    /* CSS To stylize the scrollbar */
    .scrollpane::-webkit-scrollbar {
      width: 20px;
    }
    /* Track */
    .scrollpane::-webkit-scrollbar-track {
      background-color: white;
      box-shadow: inset 0 0 5px white;
      border-radius: 10px;
    }
    /* Handle */
    .scrollpane::-webkit-scrollbar-thumb {
      background: red;
      border-radius: 10px;
    }
    /* Handle on hover */
    .scrollpane::-webkit-scrollbar-thumb:hover {
      background: #b30000;
    }

    To get the CSS into the document header, we need to first convert (“stringify”) it in Matlab:

    % CSS styles 'stringified' for MATLAB
    %  note that the whole style tag is wrapped in single quotes, that is required!
    %  i.e. '<style>...</style>' which prints to the command window as:
    %   ''<style>...</style>''
    cssText = [...
      '''<style>\n', ...
      '  .scrollpane::-webkit-scrollbar {\n', ...
      '    width: 20px;\n', ...
      '  }\n', ...
      '  /* Track */\n', ...
      '  .scrollpane::-webkit-scrollbar-track {\n', ...
      '    background-color: white;\n', ...
      '    box-shadow: inset 0 0 5px white;\n', ...
      '    border-radius: 10px;\n', ...
      '  }\n', ...
      '  /* Handle */\n', ...
      '  .scrollpane::-webkit-scrollbar-thumb {\n', ...
      '    background: red; \n', ...
      '    border-radius: 10px;\n', ...
      '  }\n', ...
      '  /* Handle on hover */\n', ...
      '  .scrollpane::-webkit-scrollbar-thumb:hover {\n', ...
      '    background: #b30000; \n', ...
      '  }\n', ...
      '</style>''' ...
      ];

    As explained in Customizing uifigures part 1, we can execute JavaScript (JS) commands through the webWindow object. To simplify it, we use the method from Customizing uifigures part 2 to obtain the webWindow object for our GUI window and and use the executeJS() method to add our HTML into the document’s <head> tag. It is worth checking out the properties and methods of the JS document object.

    % get the webWindow object
    webWindow = mlapptools.getWebWindow(fig);
     
    % inject the CSS
    webWindow.executeJS(['document.head.innerHTML += ',cssText]);

    Now the tricky part is that we have to assign our new CSS scrollpane class to our uipanel. We need 2 things: the webWindow object and the data-tag (our panel’s unique ID) attribute.

    % get the uipanel data-tag attr.
    [webWindow, panelID] = mlapptools.getWebElements(scrollPane);
     
    %make a dojo.js statement (use addClass method)
    setClassString = sprintf(...
      'dojo.addClass(dojo.query("[%s = ''%s'']")[0], "%s")',...
      panelID.ID_attr, panelID.ID_val, 'scrollpane');
     
    % add class to DOM element
    webWindow.executeJS(setClassString);

    The results of applying our scrollpane CSS style on our scroll-pane's scrollbars
    The results of applying our scrollpane CSS style on our scroll-pane’s scrollbars

    Note: Unfortunately, because of CSS precedence rules, we may have to use the dreaded !important CSS qualifier to get the desired effect. So if the result doesn’t match your expectations, try adding !important to the CSS class attributes.

    Styling the uipanel

    Each uipanel appears to be composed of 4 <div> HTML elements: a wrapper, internal container for the panel title, a separator, and the panel’s body (contents). We first use mlapptools.getWebElements() to get the data-tag ID for the wrapper node. We can then apply styles to the wrapper, or any child node, with CSS and JS.

    For example, we can use CSS3 patterns (such as one in this CSS3 gallery) to liven up our panel. We can also use CSS to define a block element that will replace the title container with some static text. The CSS below would be appended to the cssText string we made for styling the scrollbars above:

    /* DECORATE THE BACKGROUND */
    /* Stars modified from: http://lea.verou.me/css3patterns/#stars */
    .scrollpane {
      overflow: auto;
      background:
      linear-gradient(324deg, #232927 4%,   transparent 4%)   -70px 43px,
      linear-gradient( 36deg, #232927 4%,   transparent 4%)    30px 43px,
      linear-gradient( 72deg, #e3d7bf 8.5%, transparent 8.5%)  30px 43px,
      linear-gradient(288deg, #e3d7bf 8.5%, transparent 8.5%) -70px 43px,
      linear-gradient(216deg, #e3d7bf 7.5%, transparent 7.5%) -70px 23px,
      linear-gradient(144deg, #e3d7bf 7.5%, transparent 7.5%)  30px 23px,
      linear-gradient(324deg, #232927 4%,   transparent 4%)   -20px 93px,
      linear-gradient( 36deg, #232927 4%,   transparent 4%)    80px 93px,
      linear-gradient( 72deg, #e3d7bf 8.5%, transparent 8.5%)  80px 93px,
      linear-gradient(288deg, #e3d7bf 8.5%, transparent 8.5%) -20px 93px,
      linear-gradient(216deg, #e3d7bf 7.5%, transparent 7.5%) -20px 73px,
      linear-gradient(144deg, #e3d7bf 7.5%, transparent 7.5%)  80px 73px !important;
      background-color: #232977 !important;
      background-size: 100px 100px !important;
    }
    /* STYLES TO CENTER A TEXT BLOCK ON A WHITE SEMI-TRANSPARENT BACKGROUND BLOCK */
    /* White block div */
    .blockdiv {
      color: black;
      font: 25px Arial, sans-serif !important;
      background: rgba(255,255,255,0.75);
      width: 100%;
      height: 30px;
    }
    /* Text container centered in .blockdiv */
    .textdiv {
      position: relative;
      float: left;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }

    To insert a static text element and its container, we can create a small JS routine that creates parent and child nodes that replace the panel’s title container:

    % Make a nodeID string to make the JS call more generic
    nodeID = sprintf('''[%s="%s"]''', panelID.ID_attr, panelID.ID_val);
     
    % JS that creates a div within a div, each with their own classes
    % The inner div gets the text and is centered within the outer div
    % These elements are added before the node MATLAB will use for any controls
    % added to scrollPane
    js = sprintf( ...
      [ ...
        'var d = document.createElement("div");', ...
        'var t = document.createElement("div");', ...
        'd.classList.add("blockdiv");',...
        't.classList.add("textdiv");', ...
        't.innerHTML= "Some Static Text";', ...
        'd.appendChild(t);', ...
        'document.querySelectorAll(%s)[0]',...
        '.replaceChild(d,document.querySelectorAll(%s)[0].firstChild);' ...
      ], ...
      nodeID, nodeID ...
    );
     
    % execute the JS
    webWindow.executeJS(js);

    Panel background and static elements
    Panel background and static elements

    It seems to me that this approach might help to make lighter-weight apps, instead of having to make all those app.Label objects in Matlab’s App-Designer.

    Quick recap

    So let’s restate the process:

    1. Create a uipanel with the Position property set accordingly large enough for your control elements.
    2. Use mlapptools.setStyle() to set the overflow style attribute as desired.
    3. Use mlapptools.setStyle() to set the width and/or height style attributes to the viewing size (this is how big the viewing area of the panel needs to be in order to fit nicely in your app).
    4. Add your control elements with the scrolling uipanel as the parent.
    5. If you want some special styles, create a stylesheet and inject it into the <head> and be sure to add the class to your panel’s HTML classList.

    The order of items 2-4 are not really important. You just need to ensure that the panel is large enough (via its Position property) to include all your elements/controls.

    I really hope that one day soon MathWorks will add CSS and JS hooks to uifigure GUI components (perhaps as settable CSS/JS properties that accept strings?), so that Matlab users could attach their own CSS and JS customizations directly within AppDesigner, without having to go through such undocumented hoops as I’ve shown here. In Loren Shure’s latest blog post, Matlab product manager Dave Garisson indicated that this is indeed planned for a near-future Matlab release (at least for JS, but hopefully also for CSS):

    “we are also investigating ways to provide a documented solution for integrating 3rd party JavaScript components in MATLAB apps.”

    A complete working example

    I created a complete working example in Matlab’s App Designer while figuring this whole thing out. The code (CWE.m) can be downloaded here, and then run directly from Matlab’s command window. Alternatively, the corresponding App Designer file (CWE.mlapp) can be downloaded here. You are welcome to use/adapt the code in your own project. Just to be clear, I love wild colors and crazy themes, but I don’t recommend going this overboard for a real project.

    Running app demo
    Running app demo

    I can’t thank Yair enough for suggesting that I turn this tip into a guest post for you readers. And I want to give a huge thank you to you, the reader, for persevering all the way to the end of this post…

    Cheers!
    -Khris

    Addendum September 17, 2018: Scrolling panels in uifigures are now a fully-supported documented functionality via the new scroll function and Scrollable property, starting with Matlab release R2018b. You can still use the mechanism described above, which also works for older Matlab releases.

    ]]>
    https://undocumentedmatlab.com/blog/customizing-web-gui-uipanel/feed 0
    Scrollable GUI panelshttps://undocumentedmatlab.com/blog/scrollable-gui-panels https://undocumentedmatlab.com/blog/scrollable-gui-panels#comments Wed, 25 Jul 2018 09:50:31 +0000 https://undocumentedmatlab.com/?p=7824
     
    Related posts:
    1. Blurred Matlab figure window Matlab figure windows can be blurred using a semi-transparent overlaid window - this article explains how...
    2. Uitable customization report Matlab's uitable can be customized in many different ways. A detailed report explains how. ...
    3. Customizing menu items part 2 Matlab menu items can be customized in a variety of useful ways using their underlying Java object. ...
    4. Customizing menu items part 3 Matlab menu items can easily display custom icons, using just a tiny bit of Java magic powder. ...
     
    ]]>
    Matlab enables two types of GUI container types, via the Units property: fixed-size ('pixels', 'chars', etc.) and flexible ('normalized'). In many cases, we need something in between: a panel that expands dynamically when its container grows (i.e., flexible/normalized), and displays scroll-bars when the container shrinks (i.e., fixed size, with a scrollable viewport). This functionality is relatively easy to achieve using a bit of undocumented magic powder. Today’s post will show how to do this with legacy (Java-based) figures, and next week’s post will do the same for web-based (JavaScript) uifigures.
    Scrollable Matlab GUI panel
    Scrollable Matlab GUI panel

    Technical description

    The basic idea is that in HG2 (Matlab release R2014b onward), uipanels are implemented using standard Java JPanel components. This enables all sorts of interesting customizations. For the purposes of today’s discussion, the important thing to note is that the underlying JPanel object can be re-parented to reside inside a Java JScrollPanel.

    So, the idea is to get the Matlab panel’s underlying JPanel object reference, then embed it within a new JScrollPanel object that is placed at the exact same GUI coordinates as the original panel. The essential Matlab code snippet is this:

    % Create the Matlab uipanel in the GUI
    hPanel = uipanel(...); drawnow
     
    % Get the panel's underlying JPanel object reference
    jPanel = hPanel.JavaFrame.getGUIDEView.getParent;
     
    % Embed the JPanel within a new JScrollPanel object
    jScrollPanel = javaObjectEDT(javax.swing.JScrollPane(jPanel));
     
    % Remove the JScrollPane border-line
    jScrollPanel.setBorder([]);
     
    % Place the JScrollPanel in same GUI location as the original panel
    pixelpos = getpixelposition(hPanel);
    hParent = hPanel.Parent;
    [hjScrollPanel, hScrollPanel] = javacomponent(jScrollPanel, pixelpos, hParent);
    hScrollPanel.Units = 'norm';
     
    % Ensure that the scroll-panel and contained panel have linked visibility
    hLink = linkprop([hPanel,hScrollPanel],'Visible');
    setappdata(hPanel,'ScrollPanelVisibilityLink',hLink);

    Note that this code will only work with panels created in legacy figures, not web-based uifigures (as I mentioned above, a similar solution for uifigures will be presented here next week).

    Also note that the new scroll-panel is created with javaObjectEDT, in order to avoid EDT synchronization problems

    We also want to link the visibility of the scroll-panel and its contained Matlab panel (hPanel), so that when the panel is set to be non-visible (hPanel.Visible='off'), the entire scroll-panel (scrollbars included) will become invisible, and vice-versa. We can do this by linking the Visible property of the Matlab panel and the scroll-panel container (hScrollPanel) using the linkprop function at the bottom of the script above. Note that we must persist the resulting hLink otherwise it becomes defunct – this is done by using setappdata to store the link in the panel (this way, when the panel is deleted, so does the link).

    Resizing the container

    The scroll-panel is created with a specific pixelpos location and size, and then its container is made to have normalized units. This ensures that when the container (hParent) grows, the scroll-panel grows as well, and no scrollbars appear (since they are not needed). But when the container shrinks in the X and/or Y direction, corresponding scrollbars appear as-needed. It sounds complicated, but it’s actually very intuitive, as the animated image above shows.

    When the container resizes, the displayed viewport image may “jump” sideways. To fix this we can attach a simple repaint callback function to the scroll-panel’s SizeChangedFcn property:

    % Attach a repaint callback function
    hScrollPanel.SizeChangedFcn = @repaintScrollPane;
     
    % Define the callback function:
    function repaintScrollPane(hScrollPanel, varargin)
        drawnow
        jScrollPanel = hScrollPanel.JavaPeer;
        offsetX = 0; %or: jScrollPanel.getHorizontalScrollBar.getValue;
        offsetY = 0; %or: jScrollPanel.getVerticalScrollBar.getValue;
        jOffsetPoint = java.awt.Point(offsetX, offsetY);
        jViewport = jScrollPanel.getViewport;
        jViewport.setViewPosition(jOffsetPoint);
        jScrollPanel.repaint;
    end

    Scrollbars automatically appear as-needed during resize

    Scrollbars automatically appear as-needed during resize

    Viewport position/offset

    It would be convenient to have an easy-to-use ViewOffset property in the hScrollPanel object, in order to be able to programmatically query and set the current viewport position (i.e., scrollbars offset). We can add this property via the addprop function:

    % Add a new Viewoffset property to hSCrollPanel object
    hProp = addprop(hScrollPanel, 'ViewOffset');
    hProp.GetMethod = @getViewOffset;  %viewOffset = getViewOffset(hScrollPanel)
    hProp.SetMethod = @setViewOffset;  %setViewOffset(hScrollPanel, viewOffset)
     
    % Getter method for the dynamic ViewOffset property
    function viewOffset = getViewOffset(hScrollPanel, varargin)
        jScrollPanel = hScrollPanel.JavaPeer;
        jPoint = jScrollPanel.getViewport.getViewPosition;
        viewOffset = [jPoint.getX, jPoint.getY];
    end
     
    % Setter method for the dynamic ViewOffset property
    function setViewOffset(hScrollPanel, viewOffset)
        jPoint = java.awt.Point(viewOffset(1), viewOffset(2));
        jScrollPanel = hScrollPanel.JavaPeer;
        jScrollPanel.getViewport.setViewPosition(jPoint);
        jScrollPanel.repaint;
    end

    This enables us to both query and update the scroll-panel’s view position – [0,0] means top-left corner (i.e., no scroll); [12,34] mean scrolling 12 to the right and 34 down:

    >> offset = hScrollPanel.ViewOffset   % or: get(hScrollPanel,'ViewOffset')
    offset = 
         0     0
     
    >> offset = hScrollPanel.ViewOffset   % or: get(hScrollPanel,'ViewOffset')
    offset = 
        12    34
     
    % Scroll 30 pixels right, 50 pixels down
    >> hScrollPanel.ViewOffset = [30,50];   % or: set(hScrollPanel,'ViewOffset',[30,50])

    attachScrollPanelTo utility

    I have prepared a utility called attachScrollPanelTo (downloadable from the Matlab File Exchange), which encapsulates all of the above, plus a few other features: inputs validation, Viewport property in the output scroll-pane object, automatic encasing in a new panel for input object that are not already a panel, etc. Feel free to download the utility, use it in your program, and modify the source-code to fit your needs. Here are some usage examples:

    attachScrollPanelTo();  % display the demo
     
    attachScrollPanelTo(hPanel) % place the specified hPanel in a scroll-panel
     
    hScroll = attachScrollPanelTo(hPanel);
    hScroll.ViewOffset = [30,50];  % set viewport offset (30px right, 50px down)
    set(hScroll, 'ViewOffset',[30,50]);  % equivalent alternative

    If you’d like me to add flare to your Matlab GUI, don’t hesitate to contact me on my Consulting page.

    ]]>
    https://undocumentedmatlab.com/blog/scrollable-gui-panels/feed 1
    Multi-threaded Mexhttps://undocumentedmatlab.com/blog/multi-threaded-mex https://undocumentedmatlab.com/blog/multi-threaded-mex#comments Wed, 18 Jul 2018 14:56:44 +0000 https://undocumentedmatlab.com/?p=7771
     
    Related posts:
    1. Explicit multi-threading in Matlab part 3 Matlab performance can be improved by employing POSIX threads in C/C++ code. ...
    2. Faster csvwrite/dlmwrite The speed of the builtin csvwrite, dlmwrite functions can be improved dramatically. ...
    3. Matlab mex in-place editing Editing Matlab arrays in-place can be an important technique for optimizing calculations. This article shows how to do it using Mex. ...
    4. Parsing mlint (Code Analyzer) output The Matlab Code Analyzer (mlint) has a lot of undocumented functionality just waiting to be used. ...
     
    ]]>
    I was recently asked by a consulting client to help speed up a Matlab process. Accelerating MATLAB Performance Quite often there are various ways to improve the run-time, and in this particular case it turned out that the best option was to convert the core Matlab processing loop into a multi-threaded Mex function, while keeping the rest (vast majority of program code) in easy-to-maintain Matlab. This resulted in a 160x speedup (25 secs => 0.16 secs). Some of this speedup is attributed to C-code being faster in general than Matlab, another part is due to the multi-threading, and another due to in-place data manipulations that avoid costly memory access and re-allocations.

    In today’s post I will share some of the insights relating to this MEX conversion, which could be adapted for many other similar use-cases. Additional Matlab speed-up techniques can be found in other performance-related posts on this website, as well in my book Accelerating MATLAB Performance.

    There are quite a few online resources about creating Mex files, so I will not focus on this aspect. I’ll assume that the reader is already familiar with the concept of using Mex functions, which are simply dynamically-linked libraries that have a predefined entry-function syntax and predefined platform-specific extension. Instead, I’ll focus on how to create and debug a multi-threaded Mex function, so that it runs in parallel on all CPU cores.

    The benefit of multi-threading is that threads are very light-weight objects, that have minimal performance and memory overheads. This contrasts to multi-tasking, which is what the Parallel Computing Toolbox currently does: launches duplicate copies of the entire Matlab engine process (“headless workers”) and then manages and coordinates the tasks to split up the processing work. Multi-tasking should be avoided wherever we can employ light-weight multi-threading instead. Unfortunately, Matlab does not currently have the ability to explicitly multi-thread Matlab code. But we can still use explicit multi-threading by invoking code in other languages, as I’ve already shown for Java, C# (and .NET in general), and C/C++. Today’s article will expand on the latter post (the one about C/C++ multi-threading), by showing a general framework for making a multi-threaded C-based Mex function.

    There are several alternative implementation of threads. On non-Windows machines, POSIX threads (“pthreads”) are a de-facto standard; on Windows, which pthreads can still be used, they generally use native Windows threads under the hood, and we can use these native threads directly.

    I have uploaded a file called max_in_place to the Matlab File Exchange. This function serves as an example showing a generic multi-threaded Mex function. A compiled version of this Mex file for Win64 is also included in the File Exchange entry, and you can run it directly on a Win64 machine.

    The usage in Matlab is as follows (note how matrix1 is updated in-place):

    >> matrix1 = rand(4)
    matrix1 =
          0.89092      0.14929      0.81428      0.19664
          0.95929      0.25751      0.24352      0.25108
          0.54722      0.84072      0.92926      0.61604
          0.13862      0.25428      0.34998      0.47329
     
    >> matrix2 = rand(4)
    matrix2 =
          0.35166      0.91719      0.38045      0.53081
          0.83083      0.28584      0.56782      0.77917
          0.58526       0.7572     0.075854      0.93401
          0.54972      0.75373      0.05395      0.12991
     
    >> max_in_place(matrix1, matrix2)
     
    >> matrix1
    matrix1 =
          0.89092      0.91719      0.81428      0.53081
          0.95929      0.28584      0.56782      0.77917
          0.58526      0.84072      0.92926      0.93401
          0.54972      0.75373      0.34998      0.47329

    Source code and discussion

    The pseudo-code for the MEX function is as follows:

    mexFunction():
       validate the input/output args
       quick bail-out if empty inputs
       get the number of threads N from Matlab's maxNumCompThreads function
       if N == 1
           run main_loop directly
       else
           split input matrix #1 into N index blocks
           assign start/end index for each thread
           create and launch N new threads that run main_loop
           wait for all N threads to complete
           free the allocated memory resources
       end

    Here’s the core source-code of this function, which was adapted from original work by Dirk-Jan Kroon:

    /*====================================================================
     *
     * max_in_place.c  updates a data matrix in-place with the max value
     *                 of the matrix and a 2nd matrix of the same size
     *
     * The calling syntax is:
     *
     *		max_in_place(matrix1, matrix2)
     *
     * matrix1 will be updated with the maximal values from corresponding
     * indices of the 2 matrices
     *
     * Both inputs must be double 2D real non-sparse matrices of same size
     *
     * Yair Altman 2018-07-18
     * http://UndocumentedMatlab.com/blog/multi-threaded-mex
     *
     * Adapted from original work by Dirk-Jan Kroon
     * http://mathworks.com/matlabcentral/profile/authors/1097878-dirk-jan-kroon
     *
     *==================================================================*/
     
    #include <math.h>
    #include "mex.h"
     
    /* undef needed for LCC compiler */
    #undef EXTERN_C
    #ifdef _WIN32
        #include <windows.h>
        #include <process.h>
    #else
        #include <pthread.h>
    #endif
     
    /* Input Arguments */
    #define	hMatrix1	prhs[0]
    #define	hMatrix2	prhs[1]
     
    /* Macros */
    #if !defined(MAX)
    #define	MIN(A, B)	((A) < (B) ? (A) : (B))
    #endif
     
    /* Main processing loop function */
    void main_loop(const mxArray *prhs[], int startIdx, int endIdx)
    {
        /* Assign pointers to the various parameters */
        double *p1 = mxGetPr(hMatrix1);
        double *p2 = mxGetPr(hMatrix2);
     
        /* Loop through all matrix coordinates */
        for (int idx=startIdx; idx<=endIdx; idx++)
        {
            /* Update hMatrix1 with the maximal value of hMatrix1,hMatrix2 */
            if (p1[idx] < p2[idx]) {
                p1[idx] = p2[idx];
            }
        }
    }
     
    /* Computation function in threads */
    #ifdef _WIN32
      unsigned __stdcall thread_func(void *ThreadArgs_) {
    #else
      void thread_func(void *ThreadArgs_) {
    #endif
        double **ThreadArgs = ThreadArgs_;  /* void* => double** */
        const mxArray** prhs = (const mxArray**) ThreadArgs[0];
     
        int ThreadID = (int) ThreadArgs[1][0];
        int startIdx = (int) ThreadArgs[2][0];
        int endIdx   = (int) ThreadArgs[3][0];
        /*mexPrintf("Starting thread #%d: idx=%d:%d\n", ThreadID, startIdx, endIdx); */
     
        /* Run the main processing function */
        main_loop(prhs, startIdx, endIdx);
     
        /* Explicit end thread, helps to ensure proper recovery of resources allocated for the thread */
        #ifdef _WIN32
            _endthreadex( 0 );
            return 0;
        #else
            pthread_exit(NULL);
        #endif
    }
     
    /* validateInputs function here... */
     
    /* Main entry function */
    void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
     
    {
        /* Validate the inputs */
        validateInputs(nlhs, plhs, nrhs, prhs);
     
        /* Quick bail-out in the trivial case of empty inputs */
        if (mxIsEmpty(hMatrix1))  return;
     
        /* Get the number of threads from the Matlab engine (maxNumCompThreads) */
        mxArray *matlabCallOut[1] = {0};
        mxArray *matlabCallIn[1]  = {0};
        mexCallMATLAB(1, matlabCallOut, 0, matlabCallIn, "maxNumCompThreads");
        double *Nthreadsd = mxGetPr(matlabCallOut[0]);
        int Nthreads = (int) Nthreadsd[0];
     
        /* Get the number of elements to process */
        size_t n1 = mxGetNumberOfElements(hMatrix1);
     
        if (Nthreads == 1) {
     
            /* Process the inputs directly (not via a thread) */
            main_loop(prhs, 0, n1-1);
     
        } else {  /* multi-threaded */
     
            /* Allocate memory for handles of worker threads */
            #ifdef _WIN32
                HANDLE    *ThreadList = (HANDLE*)   malloc(Nthreads*sizeof(HANDLE));
            #else
                pthread_t *ThreadList = (pthread_t*)malloc(Nthreads*sizeof(pthread_t));
            #endif
     
            /* Allocate memory for the thread arguments (attributes) */
            double **ThreadID, **ThreadStartIdx, **ThreadEndIdx, ***ThreadArgs;
            double *ThreadID1, *ThreadStartIdx1, *ThreadEndIdx1, **ThreadArgs1;
     
            ThreadID       = (double **) malloc( Nthreads* sizeof(double *) );
            ThreadStartIdx = (double **) malloc( Nthreads* sizeof(double *) );
            ThreadEndIdx   = (double **) malloc( Nthreads* sizeof(double *) );
            ThreadArgs     = (double ***)malloc( Nthreads* sizeof(double **) );
     
            /* Launch the requested number of threads */
            int i;
            int threadBlockSize = ceil( ((double)n1) / Nthreads );
            for (i=0; i<Nthreads; i++)
            {
                /* Create thread ID */
                ThreadID1 = (double *)malloc( 1* sizeof(double) );
                ThreadID1[0] = i;
                ThreadID[i] = ThreadID1;
     
                /* Compute start/end indexes for this thread */
                ThreadStartIdx1 = (double *) malloc( sizeof(double) );
                ThreadStartIdx1[0] = i * threadBlockSize;
                ThreadStartIdx[i] = ThreadStartIdx1;
     
                ThreadEndIdx1 = (double *) malloc( sizeof(double) );
                ThreadEndIdx1[0] = MIN((i+1)*threadBlockSize, n1) - 1;
                ThreadEndIdx[i] = ThreadEndIdx1;
     
                /* Prepare thread input args */
                ThreadArgs1 = (double **) malloc( 4* sizeof(double*) );
                ThreadArgs1[0] = (double *) prhs;
                ThreadArgs1[1] = ThreadID[i];
                ThreadArgs1[2] = ThreadStartIdx[i];
                ThreadArgs1[3] = ThreadEndIdx[i];
     
                ThreadArgs[i] = ThreadArgs1;
     
                /* Launch the thread with its associated args */
                #ifdef _WIN32
                    ThreadList[i] = (HANDLE)_beginthreadex(NULL, 0, &thread_func, ThreadArgs[i], 0, NULL);
                #else
                    pthread_create ((pthread_t*)&ThreadList[i], NULL, (void *) &thread_func, ThreadArgs[i]);
                #endif
            }
     
            /* Wait for all the treads to finish working */
            #ifdef _WIN32
                for (i=0; i<Nthreads; i++) { WaitForSingleObject(ThreadList[i], INFINITE); }
                for (i=0; i<Nthreads; i++) { CloseHandle( ThreadList[i] ); }
            #else
                for (i=0; i<Nthreads; i++) { pthread_join(ThreadList[i],NULL); }
            #endif
     
            /* Free the memory resources allocated for the threads */
            for (i=0; i<Nthreads; i++)
            {
                free(ThreadArgs[i]);
                free(ThreadID[i]);
                free(ThreadStartIdx[i]);
                free(ThreadEndIdx[i]);
            }
     
            free(ThreadArgs);
            free(ThreadID );
            free(ThreadStartIdx);
            free(ThreadEndIdx);
            free(ThreadList);
        }
     
        return;
    }

    This file also includes a validateInputs function. I did not include it in the code snippet above for brevity; you can read it directly in the FEX entry (max_in_place.c). This function checks that there are exactly 0 output and 2 input args, that the input args are real non-sparse matrices and that they have the same number of elements.

    Note that the threads run a generic thread_func function, which in turn runs the main_loop function with the thread’s specified startIndex, endIndex values. When this function completes, the thread ends itself explicitly, to ensure resource cleanup.

    Also note how the thread code is using pthreads on non-Windows (!defined(_WIN32)) machines, and native Windows threads otherwise. This means that the same MEX source-code could be used on all Matlab platforms.

    The important thing to note about this framework is that we no longer need to worry about the thread plumbing. If we wish to adapt this code for any other processing, we just need to modify the main_loop function with the new processing logic. In addition, you may wish to modify the validateInputs function based on your new setup of input/output args.

    A few caveats:

    • On Windows machines with R2017b or older, we simply compile using mex max_in_place.c; on non-Windows we might need to add the –lpthread flag to link the pthreads library, depending on your specific compiler.
    • On R2018a or newer on all platforms, due to MEX’s new interleaved-complex memory format, we would need to compile with the -R2017b flag if we wish to use mexGetPr, as in the sample code above (in R2018a’s new data model, the corresponding function is mxGetDoubles). Note that updating data in-place becomes more difficult with the new MEX API, so if you want to preserve the performance boost that in-place data manipulation provides, it may be better to stick with the legacy data memory model.
    • The sample code above splits the data between the threads based on the first input matrix’s size. Instead, you may consider sending to the MEX function the loop indexes as extra input args, and then splitting those up between the threads.
    • In this specific implementation of max_in_place, I have updated the data locations directly. This is generally discouraged and risky, because it conflicts with Matlab’s standard Copy-on-Write mechanism. For example, if we assign the input to any other Matlab variable(s) before calling max_in_place, then that other variable(s) will also get their data updated. If we do not want this side-effect, we should mxUnshareArray the input matrix1, and return the resulting matrix as an output of the MEX function (plhs[0]).

    Speed-up tips

    The core logic in the specific case that I was asked to optimize was something similar to this:

    main_process:
        initialize output matrix
        loop z over all slices in a 3D data matrix
            temp_data = data(:,:,z);
            temp_data = process(temp_data);
            output = max(output, temp_data);
        end z loop

    The initial speed-up attempt was naturally focused on the process and max functions. Converting them to a MEX function improved the speed by a factor of ~8, but the resulting run-time (4-5 secs) was still too slow for real-time use. The reason that we did not see a larger speed-up was, I believe, due to several reasons:

    • temp_data was small enough such that the overheads associated with creating and then disposing separate threads were significant compared to the processing time of each thread.
    • temp_data was small enough such that each thread processed a relatively small portion of the memory, in contrast to single-threaded processing that accesses memory in larger blocks, more efficiently.
    • In each iteration of the z loop, the overheads associated with calling the MEX function, handling input variables and validation, creating/disposing threads, and allocating/deallocating memory for temp_data, were repeatedly paid.

    So, while the profiling result showed that 98% of the time was spent in the MEX function (which would seem to indicate that not much additional speedup can be achieved), in fact the MEX function was under-performing because of the inefficiencies involved in repeatedly creating threads to process small data chunks. It turned out that running in single-thread mode was actually somewhat faster than multi-threaded mode.

    I then moved the entire z loop (entire main_process) into the MEX function, where the threads were split to process separate adjacent blocks of z slices (i.e. different blocks of the z loop). This way, the MEX function was called, the inputs validated, and threads created/disposed only once for the entire process, making this overhead negligible compared to each thread’s processing time. Moreover, each thread now processed the entire temp_data belonging to its z slice, so memory access was more efficient, reducing the memory I/O wait time and improving the overall processing time. Additional benefits were due to the fact that some variables could be reused within each thread across loop iterations, minimizing memory allocations and deallocations. The overall effect was to reduce the total run-time down to ~0.16 secs, a 160x speedup compared to the original (25 secs). As my client said: “You sped up [the application] from practically useless to clinically useful.”

    The lesson: try to minimize MEX invocation and thread creation/disposal overheads, and let the threads process as large adjacent memory blocks as possible.

    Debugging MEX files

    When debugging multi-threaded MEX functions, I find that it’s often easier to run the function in single-threaded mode to debug the core logic, and once this is ready we can switch back multi-threading. This can easily be done by setting the number of threads outside the MEX function using Matlab’s builtin maxNumCompThreads function:

    Nthreads = maxNumCompThreads(1);  % temporarily use only 1 thread for debugging
    max_in_place(matrix1, matrix2);
    maxNumCompThreads(Nthreads);      % restore previous value
    %maxNumCompThreads('automatic');  % alternative

    Once debugging is done and the MEX function works properly, we should remove the maxNumCompThreads calls, so that the MEX function will use the regular number of Matlab computational threads, which should be the same as the number of cores: feature(‘numCores’).

    I typically like to use Eclipse as my IDE for non-Matlab code (Java, C/C++ etc.). Unfortunately, there’s a problem attaching Eclipse to Matlab processes (which is necessary for interactive MEX debugging) if you’re using any recent (post-2015) version of MinGW and Eclipse. This problem is due to a known Eclipse bug, as user Lithe pointed out. The workaround is to install an old version of MinGW, *in addition* to your existing MinGW version. Reportedly, only versions 4.9.1 or older of MinGW include gdb 7.8 (which is still supported by Eclipse), whereas newer versions of MinGW include a newer gdb that is not supported. Download and install such an old MinGW version in a separate folder from your more-modern compiler. Don’t update your MEX to use the older MinGW – just tell Eclipse to use the version of gdb in the old MinGW bin/ folder when you set up a debug configuration for debugging your MEX files.

    Once you have a compatible gdb, and ask Eclipse to attach to a process, the processes list will finally appear (it won’t with an incompatible gdb). Use feature('getPID') to get your Matlab process ID, which can then used to attach to the relevant process in the Eclipse Attach-to-process window. For example, if your Matlab’s PID is 4321, then the Matlab process will be called “Program – 4321” in Eclipse’s processes list.

    I wish that MathWorks would update their official Answer and their MinGW support package on File Exchange to include this information, because without it debugging on Eclipse becomes impossible. Eclipse is so powerful, easy-to-use and ubiquitous that it’s a shame for most users not to be able to work with it just because the workarounds above are not readily explained.

    N.B. If you don’t like Eclipse, you can also use Visual Studio Code (VS Code), as Andy Campbell recently explained in the MathWorks Developers’ blog.

    Consulting

    Do you have any Matlab code that could use a bit (or a lot) of speeding-up? If so, please contact me for a private consulting offer. I can’t promise to speed up your code by a similar factor of 160x, but you never know…

    ]]>
    https://undocumentedmatlab.com/blog/multi-threaded-mex/feed 3
    Plot legend customizationhttps://undocumentedmatlab.com/blog/plot-legend-customization https://undocumentedmatlab.com/blog/plot-legend-customization#comments Thu, 12 Jul 2018 14:11:40 +0000 https://undocumentedmatlab.com/?p=7744
     
    Related posts:
    1. Plot LineSmoothing property LineSmoothing is a hidden and undocumented plot line property that creates anti-aliased (smooth unpixelized) lines in Matlab plots...
    2. Multi-column (grid) legend This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
    3. Controlling plot data-tips Data-tips are an extremely useful plotting tool that can easily be controlled programmatically....
    4. getundoc – get undocumented object properties getundoc is a very simple utility that displays the hidden (undocumented) properties of a specified handle object....
     
    ]]>
    Three years ago I explained how we can use a couple of undocumented hidden properties of the legend in order to add a legend title (the legend object had no Title property back then – this was only added in a later Matlab release, perhaps as a result of my post). Today I will expand on that article by explaining the plot legend’s internal graphics hierarchy, how we can access each of these components, and then how this information could be used to customize the separate legend components. Note that the discussion today is only relevant for HG2 legends (i.e. R2014b or newer).

    Let’s start with a simple Matlab plot with a legend:

    hold all; 
    hLine1 = plot(1:5); 
    hLine2 = plot(2:6); 
    hLegend = legend([hLine1,hLine2], 'Location','SouthEast');
    hLegend.Title.String = 'MyLegend';

    Standard Matlab legend

    Standard Matlab legend

    This legend is composed of the following visible internal components, which can be customized separately:
    Matlab legend components


    Id in screenshotAccessed viaObject typeDescriptionImportant properties
    1hLegend.TitleTextTitle of the legendVisible, String, Color, FontSize, FontWeight.
    2hLegend.TitleSeparatorLineStripSeparator line between title and legend entries. Only appears when title is set.Visible, LineStyle, LineWidth, ColorData (4×1 uint8)
    3hLegend.BoxEdgeLineLoopBox (border) line around the entire legend (including title)Visible, LineStyle, LineWidth, ColorData (4×1 uint8)
    4hLegend.EntryContainer.NodeChildren(2)LegendEntryEntry row in the legend, corresponding to hLine1Icon, Label, Object (line object in main axes)
    5hLegend.EntryContainer.NodeChildren(1)LegendEntryEntry row in the legend, corresponding to hLine2Icon, Label, Object (line object in main axes)
    6hLegend.EntryContainer.NodeChildren(1).LabelTextLabel of legend entryVisible, String, Color, FontSize, FontWeight
    7hLegend.EntryContainer.NodeChildren(1).IconLegendIconIcon/marker of legend entryVisible, Transform.Children.Children (LineStrip object)

    A pivotal object of the legend group are the LegendEntry items, one per legend row:

    >> hLegendEntry = hLegend.EntryContainer.NodeChildren(1);
    >> get(hLegendEntry)
                  Children: [3×1 Graphics]
                     Color: [0 0 0]
                     Dirty: 0
                 FontAngle: 'normal'
                  FontName: 'Helvetica'
                  FontSize: 8
                FontWeight: 'normal'
          HandleVisibility: 'on'
                   HitTest: 'on'
                      Icon: [1×1 LegendIcon]
                     Index: 0
               Interpreter: 'tex'
                     Label: [1×1 Text]
                LayoutInfo: [1×1 matlab.graphics.illustration.legend.ItemLayoutInfo]
                    Legend: [1×1 Legend]
                  Listener: [1×1 event.listener]
                    Object: [1×1 Line]
                   Overlay: [1×1 TriangleStrip]
              OverlayAlpha: 0.65
                    Parent: [1×1 Group]
               PeerVisible: 'on'
             PickableParts: 'visible'
                  Selected: 'off'
        SelectionHighlight: 'on'
                   Visible: 'on'
           VisibleListener: [1×1 event.proplistener]

    Each LegendEntry contains a back-reference to the original graphics object. In my example above, hLegend.EntryContainer.NodeChildren(2).Object == hLine1, and hLegend.EntryContainer.NodeChildren(2).Object == hLine1. Note how the default legend entries order is the reverse of the order of creation of the original graphics objects. Naturally, we can modify this order by creating the legend py passing it an array of handles that is ordered differently (see the documentation of the legend function).

    To get all the original graphic objects together, in a single array, we could use one of two mechanisms (note the different order of the returned objects):

    % Alternative #1
    >> [hLegend.EntryContainer.NodeChildren.Object]'
    ans = 
      2×1 Line array:
     
      Line    (data2)
      Line    (data1)
     
    % Alternative #2
    >> hLegend.PlotChildren
    ans = 
      2×1 Line array:
     
      Line    (data1)
      Line    (data2)

    For some reason, accessing the displayed graphic line in LegendEntry‘s Icon is not simple. For example, the LineStrip object that corresponds to hLine2 can be gotten via:

    hLegendEntry = hLegend.EntryContainer.NodeChildren(1);
    hLegendIconLine = hLegendEntry.Icon.Transform.Children.Children;  % a LineStrip object in our example

    I assume that this was done to enable non-standard icons for patches and other complex objects (in which case the displayed icon would not necessarily be a LineStrip object). In the case of a line with markers, for example, hLegendIconLine would be an array of 2 objects: a LineStrip object and a separate Marker object. Still, I think that a direct reference in a hLegend.EntryContainer.NodeChildren(1).Icon property would have helped in 99% of all cases, so that we wouldn’t need to pass through the Transform object.

    Anyway, once we have this object reference(s), we can modify its/their properties. In the case of a LineStrip this includes LineStyle, LineWidth, ColorData (4×1 uint8), and VertexData (which controls position/length):

    >> get(hLegendIconLine(end))  % LineStrip
              AlignVertexCenters: 'on'
                 AmbientStrength: 0.3
                    ColorBinding: 'object'
                       ColorData: [4×1 uint8]
                       ColorType: 'truecolor'
                 DiffuseStrength: 0.6
                HandleVisibility: 'on'
                         HitTest: 'off'
                           Layer: 'middle'
                         LineCap: 'none'
                        LineJoin: 'round'
                       LineStyle: 'solid'
                       LineWidth: 0.5
                   NormalBinding: 'none'
                      NormalData: []
                          Parent: [1×1 Group]
                   PickableParts: 'visible'
        SpecularColorReflectance: 1
                SpecularExponent: 10
                SpecularStrength: 0.9
                       StripData: []
                         Texture: [0×0 GraphicsPlaceholder]
                      VertexData: [3×2 single]
                   VertexIndices: []
                         Visible: 'on'
           WideLineRenderingHint: 'software'

    and in the presense of markers:

    >> get(hLegendIconLine(1))  % Marker
        EdgeColorBinding: 'object'
           EdgeColorData: [4×1 uint8]
           EdgeColorType: 'truecolor'
        FaceColorBinding: 'object'
           FaceColorData: []
           FaceColorType: 'truecolor'
        HandleVisibility: 'on'
                 HitTest: 'off'
                   Layer: 'middle'
               LineWidth: 0.5
                  Parent: [1×1 Group]
           PickableParts: 'visible'
                    Size: 6
             SizeBinding: 'object'
                   Style: 'circle'
              VertexData: [3×1 single]
           VertexIndices: []
                 Visible: 'on'

    An additional undocumented legend property that is of interest is ItemTokenSize. This is a 2-element numeric array specifying the minimal size of the legend entries’ icon and label. By default hLegend.ItemTokenSize == [30,18], but we can either expand or shrink the icons/labels by setting different values. For example:

    hLegend.ItemTokenSize == [10,1];  % shrink legend icons and labels

    Note that regardless of the amount that we specify, the actual amount that will be used will be such that all legend labels appear.
    Fun: try playing with negative values for the icon and the label and see what happens :-)

    Have you come across any other interesting undocumented aspect of Matlab legends? If so, then please share it in a comment below.

    ]]>
    https://undocumentedmatlab.com/blog/plot-legend-customization/feed 3
    Sliders in Matlab GUI – part 2https://undocumentedmatlab.com/blog/sliders-in-matlab-gui-part-2 https://undocumentedmatlab.com/blog/sliders-in-matlab-gui-part-2#comments Thu, 05 Jul 2018 11:40:56 +0000 https://undocumentedmatlab.com/?p=7728
     
    Related posts:
    1. Context-Sensitive Help Matlab has a hidden/unsupported built-in mechanism for easy implementation of context-sensitive help...
    2. Customizing uitree This article describes how to customize Matlab GUI tree controls created using the undocumented uitree function...
    3. Tab panels – uitab and relatives This article describes several undocumented Matlab functions that support tab-panels...
    4. The javacomponent function Matlab's built-in javacomponent function can be used to display Java components in Matlab application - this article details its usages and limitations...
     
    ]]>
    Exactly 3 years ago I posted about various alternatives for embedding sliders in Matlab GUI. Today I will follow up on that post with a description of yet another undocumented builtin alternative – controllib.widget.Slider. A summary of the various alternatives can be seen in the following screenshot:

    Slider alternatives in Matlab GUI

    Slider alternatives in Matlab GUI

    The controllib.widget.Slider component is a class in Matlab’s internal controllib package (last week I discussed a different utility function in this package, controllib.internal.util.hString2Char).

    controllib.widget.Slider accepts 3 input arguments: containing figure handle, position in pixels, and data values. For example:

    >> hSlider = controllib.widget.Slider(gcf, [10,10,100,50], 5:25)
    hSlider = 
      Slider with properties:
     
            Data: [6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25]
           Index: 11
           Value: 15
        FontSize: 8
        Position: [10 10 100 50]

    This creates an invisible axes at the specified figure location and displays graphic axes objects that provide the appearance of the slider. When you move the slider’s knob, or click its centerline or arrows (“Steppers”), the slider’s value changes accordingly.

    You can attach a callback function to the slider as follows:

    myCallback = @(h,e) disp(h.Value);  % as an example
    addlistener(hSlider, 'ValueChanged', myCallback);

    Note that controllib.widget.Slider is based on pure-Matlab code and fully-supported functionality. The Matlab source code is available (%matlabroot%/toolbox/shared/controllib/graphics/+controllib/+widget/Slider.m) and quite readable. So while it does not actually work with the new web-based uifigures, is should be relatively easy to adapt the code so that this component could be displayed in such uifigures.

    Below is a script to recreate the screenshot above. Note the two alternative mechanisms for setting properties (Java setter-method notation, and HG set notation):

    hFig = figure('Color','w');
     
    % 1. controllib.widget.Slider
    hSlider1 = controllib.widget.Slider(hFig, [10,10,100,50], 1:20);
    hSlider1.Value = 12;
     
    % 2. uicontrol
    hSlider2 = uicontrol('style','slider', 'units','pixels', 'pos',[10,80,100,20], 'Min',0', 'Max',20, 'Value',12);
     
    % 3. JScrollBar
    jSlider3 = javaObjectEDT(javax.swing.JScrollBar);
    jSlider3.setOrientation(jSlider3.HORIZONTAL);  % Java setter-method notation
    set(jSlider3, 'VisibleAmount',1, 'Minimum',0, 'Maximum',20, 'Value',12);  % HG set notation
    [hSlider3, hContainer3] = javacomponent(jSlider3, [10,130,100,20], hFig);
     
    % 4. JSlider #1
    jSlider4 = javaObjectEDT(javax.swing.JSlider(0,20,12))
    jSlider4.setBackground(java.awt.Color.white);  % Java setter-method notation
    set(jSlider4, 'MinorTickSpacing',1, 'MajorTickSpacing',4, 'SnapToTicks',true, 'PaintLabels',true);  % HG set notation
    [hSlider4, hContainer4] = javacomponent(jSlider4, [10,180,100,30], hFig);
     
    % 5. JSlider #2
    jSlider5 = javaObjectEDT(javax.swing.JSlider(0,20,12))
    jSlider5.setBackground(java.awt.Color.white);  % Java setter-method notation
    jSlider5.setPaintTicks(true);
    set(jSlider5, 'MinorTickSpacing',1, 'MajorTickSpacing',4, 'SnapToTicks',true, 'PaintLabels',true);  % HG set notation
    [hSlider5, hContainer5] = javacomponent(jSlider5, [10,230,100,40], hFig);

    For additional details regarding the other slider alternatives, please refer to my earlier post on this subject.

    Have you ever used another interesting utility or class in Matlab’s builtin packages? If so, please tell us about it in a comment below.

    ]]>
    https://undocumentedmatlab.com/blog/sliders-in-matlab-gui-part-2/feed 6
    String/char compatibilityhttps://undocumentedmatlab.com/blog/string-char-compatibility https://undocumentedmatlab.com/blog/string-char-compatibility#comments Thu, 28 Jun 2018 12:57:59 +0000 https://undocumentedmatlab.com/?p=7710
     
    Related posts:
    1. Undocumented mouse pointer functions Matlab contains several well-documented functions and properties for the mouse pointer. However, some very-useful functions have remained undocumented and unsupported. This post details their usage....
    2. Matlab layout managers: uicontainer and relatives Matlab contains a few undocumented GUI layout managers, which greatly facilitate handling GUI components in dynamically-changing figures....
    3. Tab panels – uitab and relatives This article describes several undocumented Matlab functions that support tab-panels...
    4. uitree This article describes the undocumented Matlab uitree function, which displays data in a GUI tree component...
     
    ]]>
    In numerous functions that I wrote over the years, some input arguments were expected to be strings in the old sense, i.e. char arrays for example, 'on' or 'off'. Matlab release R2016b introduced the concept of string objects, which can be created using the string function or [starting in R2017a] double quotes ("on").

    The problem is that I have numerous functions that supported the old char-based strings but not the new string objects. If someone tries to enter a string object ("on") as input to a function that expects a char-array ('on'), in many cases Matlab will error. This by itself is very unfortunate – I would have liked everything to be fully backward-compatible. But unfortunately this is not the case: MathWorks did invest effort in making the new strings backward-compatible to some degree (for example, graphic object property names/values and many internal functions that now accept either form as input). However, backward compatibility of strings is not 100% perfect.

    In such cases, the only solution is to make the function accept both forms (char-arrays and string objects), for example, by type-casting all such inputs as char-arrays using the builtin char function. If we do this at the top of our function, then the rest of the function can remain unchanged. For example:

    function test(stage)
       if isa(stage,'string')      stage = char(stage);   end   % from this point onward, we don't need to worry about string inputs - any such strings will become plain-ol' char-arrays
     
       switch stage
          case 'stage 1', ...
          case 'stage 2', ...
          ...
       end
    end

    That was simple enough. But what if our function expects complex inputs (cell-arrays, structs etc.) that may contain strings in only some of their cells/fields?

    Luckily, Matlab contains an internal utility function that can help us: controllib.internal.util.hString2Char. This function, whose Matlab source-code is available (%matlabroot%/toolbox/shared/controllib/general/+controllib/+internal/+util/hString2Char.m) recursively scans the input value and converts any string object into the corresponding char-array, leaving all other data-types unchanged. For example:

    >> controllib.internal.util.hString2Char({123, 'char-array', "a string"})
    ans =
      1×3 cell array
        {[123]}    {'char-array'}    {'a string'}
     
    >> controllib.internal.util.hString2Char(struct('a',"another string", 'b',pi))
    ans = 
      struct with fields:
        a: 'another string'
        b: 3.14159265358979

    In order to keep our code working not just on recent releases (that support strings and controllib.internal.util.hString2Char) but also on older Matlab releases (where they did not exist), we simply wrap the call to hString2Char within a trycatch block. The adaptation of our function might then look as follows:

    function test(varargin)
       try varargin = controllib.internal.util.hString2Char(varargin); catch, end   % from this point onward, we don't need to worry about string inputs - any such strings will become plain-ol' char-arrays
       ...
    end

    Note that controllib.internal.util.hString2Char is a semi-documented function: it contains a readable internal help section (accessible via help controllib.internal.util.hString2Char), but not a doc-page. Nor is this function mentioned anywhere in Matlab’s official documentation. I think that this is a pity, because it’s such a useful little helper function.

    ]]>
    https://undocumentedmatlab.com/blog/string-char-compatibility/feed 16
    Blocked wait with timeout for asynchronous eventshttps://undocumentedmatlab.com/blog/blocked-wait-with-timeout-for-asynchronous-events https://undocumentedmatlab.com/blog/blocked-wait-with-timeout-for-asynchronous-events#respond Sun, 13 May 2018 20:22:08 +0000 https://undocumentedmatlab.com/?p=7620
     
    Related posts:
    1. Matlab-Java memory leaks, performance Internal fields of Java objects may leak memory - this article explains how to avoid this without sacrificing performance. ...
    2. File deletion memory leaks, performance Matlab's delete function leaks memory and is also slower than the equivalent Java function. ...
    3. Converting Java vectors to Matlab arrays Converting Java vectors to Matlab arrays is pretty simple - this article explains how....
    4. The Java import directive The import function can be used to clarify Java code used in Matlab. ...
     
    ]]>
    Readers of this website may have noticed that I have recently added an IQML section to the website’s top menu bar. IQML is a software connector that connects Matlab to DTN’s IQFeed, a financial data-feed of live and historic market data. IQFeed, like most other data-feed providers, sends its data in asynchronous messages, which need to be processed one at a time by the receiving client program (Matlab in this case). I wanted IQML to provide users with two complementary modes of operation:

    IQML's IQFeed-Matlab connectivity

    • Streaming (asynchronous, non-blocking) – incoming server data is processed by internal callback functions in the background, and is made available for the user to query at any later time.
    • Blocking (synchronously waiting for data) – in this case, the main Matlab processing flows waits until the data arrives, or until the specified timeout period has passed – whichever comes first.

    Implementing streaming mode is relatively simple in general – all we need to do is ensure that the underlying connector object passes the incoming server messages to the relevant Matlab function for processing, and ensure that the user has some external way to access this processed data in Matlab memory (in practice making the connector object pass incoming data messages as Matlab callback events may be non-trivial, but that’s a separate matter – read here for details).

    In today’s article I’ll explain how we can implement a blocking mode in Matlab. It may sound difficult but it turns out to be relatively simple.

    I had several requirements/criteria for my blocked-wait implementation:

    1. Compatibility – It had to work on all Matlab platforms, and all Matlab releases in the past decade (which rules out using Microsoft Dot-NET objects)
    2. Ease-of-use – It had to work out-of-the-box, with no additional installation/configuration (which ruled out using Perl/Python objects), and had to use a simple interface function
    3. Timeout – It had to implement a timed-wait, and had to be able to tell whether the program proceeded due to a timeout, or because the expected event has arrived
    4. Performance – It had to have minimal performance overhead

    The basic idea

    The basic idea is to use Matlab’s builtin waitfor, as I explained in another post back in 2012. If our underlying connector object has some settable boolean property (e.g., Done) then we can set this property in our event callback, and then waitfor(object,'Done'). The timeout mechanism is implemented using a dedicated timer object, as follows:

    % Wait for data updates to complete (isDone = false if timeout, true if event has arrived)
    function isDone = waitForDone(object, timeout)
        % Initialize: timeout flag = false
        object.setDone(false);
     
        % Create and start the separate timeout timer thread
        hTimer = timer('TimerFcn',@(h,e)object.setDone(true), 'StartDelay',timeout);
        start(hTimer);
     
        % Wait for the object property to change or for timeout, whichever comes first
        waitfor(object,'Done');
     
        % waitfor is over - either because of timeout or because the data changed
        % To determine which, check whether the timer callback was activated
        isDone = isvalid(hTimer) && hTimer.TasksExecuted == 0;
     
        % Delete the timer object
        try stop(hTimer);   catch, end
        try delete(hTimer); catch, end
     
        % Return the flag indicating whether or not timeout was reached
    end  % waitForDone

    where the event callback is responsible for invoking object.setDone(true) when the server data arrives. The usage would then be similar to this:

    requestDataFromServer();
     
    if isBlockingMode
       % Blocking mode - wait for data or timeout (whichever comes first)
       isDone = waitForDone(object, 10.0);  % wait up to 10 secs for data to arrive
       if ~isDone  % indicates a timeout
          fprintf(2, 'No server data has arrived within the specified timeout period!\n')
       end
    else
       % Non-blocking (streaming) mode - continue with regular processing
    end

    Using a stand-alone generic signaling object

    But what can we do if we don’t have such a Done property in our underlying object, or if we do not have programmatic access to it?

    We could create a new non-visible figure and then waitfor one of its properties (e.g. Resize). The property would be initialized to 'off', and within both the event and timer callbacks we would set it to 'on', and then waitfor(hFigure,'Resize','on'). However, figures, even if non-visible, are quite heavy objects in terms of both memory, UI resources, and performance.

    It would be preferable to use a much lighter-weight object, as long as it abides by the other criteria above. Luckily, there are numerous such objects in Java, which is bundled in every Matlab since 2000, on every Matlab platform. As long as we choose a small Java object that has existed forever, we should be fine. For example, we could use a simple javax.swing.JButton and its boolean property Enabled:

    hSemaphore = handle(javax.swing.JButton);  % waitfor() expects a handle() object, not a "naked" Java reference
     
    % Wait for data updates to complete (isDone = false if timeout, true if event has arrived)
    function isDone = waitForDone(hSemaphore, timeout)
        % Initialize: timeout flag = false
        hSemaphore.setEnabled(false);
     
        % Create and start the separate timeout timer thread
        hTimer = timer('TimerFcn',@(h,e)hSemaphore.setEnabled(true), 'StartDelay',timeout);
        start(hTimer);
     
        % Wait for the object property to change or for timeout, whichever comes first
        waitfor(hSemaphore,'Enabled');
     
        % waitfor is over - either because of timeout or because the data changed
        % To determine which, check whether the timer callback was activated
        isDone = isvalid(hTimer) && hTimer.TasksExecuted == 0;
     
        % Delete the timer object
        try stop(hTimer);   catch, end
        try delete(hTimer); catch, end
     
        % Return the flag indicating whether or not timeout was reached
    end  % waitForDone

    In this implementation, we would need to pass the hSemaphore object handle to the event callback, so that it would be able to invoke hSemaphore.setEnabled(true) when the server data has arrived.

    Under the hood, note that Enabled is not a true “property” of javax.swing.JButton, but rather exposes two simple public getter/setter methods (setEnabled() and isEnabled()), which Matlab interprets as a “property”. For all intents and purposes, in our Matlab code we can treat Enabled as a property of javax.swing.JButton, including its use by Matlab’s builtin waitfor function.

    There is a light overhead to this: on my laptop the waitfor function returns ~20 mSecs after the invocation of hSemaphore.setEnabled(true) in the timer or event callback – in many cases this overhead is negligible compared to the networking latencies for the blocked data request. When event reactivity is of utmost importance, users can always use streaming (non-blocking) mode, and process the incoming data events immediately in a callback.

    Of course, it would have been best if MathWorks would add a timeout option and return value to Matlab’s builtin waitfor function, similar to my waitForDone function – this would significantly simplify the code above. But until this happens, you can use waitForDone pretty-much as-is. I have used similar combinations of blocking and streaming modes with multiple other connectors that I implemented over the years (Interactive Brokers, CQG, Bloomberg and Reuters for example), and the bottom line is that it actually works well in practice.

    Let me know if you’d like me to assist with your own Matlab project or data connector, either developing it from scratch or improving your existing code. I will be visiting Boston and New York in early June and would be happy to meet you in person to discuss your specific needs.

    ]]>
    https://undocumentedmatlab.com/blog/blocked-wait-with-timeout-for-asynchronous-events/feed 0
    Speeding-up builtin Matlab functions – part 2https://undocumentedmatlab.com/blog/speeding-up-builtin-matlab-functions-part-2 https://undocumentedmatlab.com/blog/speeding-up-builtin-matlab-functions-part-2#comments Sun, 06 May 2018 16:26:19 +0000 https://undocumentedmatlab.com/?p=7533
     
    Related posts:
    1. Speeding-up builtin Matlab functions – part 1 Built-in Matlab functions can often be profiled and optimized for improved run-time performance. This article shows a typical example. ...
    2. Solving a MATLAB bug by subclassing Matlab's Image Processing Toolbox's impoint function contains an annoying bug that can be fixed using some undocumented properties....
    3. Internal Matlab memory optimizations Copy-on-write and in-place data manipulations are very useful Matlab performance improvement techniques. ...
    4. Improving save performance There are many different ways of improving Matlab's standard save function performance. ...
     
    ]]>
    Last week I showed how we can speed-up built-in Matlab functions, by creating local copies of the relevant m-files and then optimizing them for improved speed using a variety of techniques.Accelerating MATLAB Performance Today I will show another example of such speed-up, this time of the Financial Toolbox’s maxdrawdown function, which is widely used to estimate the relative risk of a trading strategy or asset. One might think that such a basic indicator would be optimized for speed, but experience shows otherwise. In fact, this function turned out to be the main run-time performance hotspot for one of my clients. The vast majority of his code was optimized for speed, and he naturally assumed that the built-in Matlab functions were optimized as well, but this was not the case. Fortunately, I was able to create an equivalent version that was 30-40 times faster (!), and my client remains a loyal Matlab fan.

    In today’s post I will show how I achieved this speed-up, using different methods than the ones I showed last week. A combination of these techniques can be used in a wide range of other Matlab functions. Additional speed-up techniques can be found in other performance-related posts on this website, as well in my book Accelerating MATLAB Performance.

    Profiling

    As I explained last week, the first step in speeding up any function is to profile its current run-time behavior using Matlab’s builtin Profiler tool, which can either be started from the Matlab Editor toolstrip (“Run and Time”) or via the profile function.

    The profile report for the client’s function showed that it had two separate performance hotspots:

    1. Code that checks the drawdown format (optional 2nd input argument) against a set of allowed formats
    2. Main code section that iteratively loops over the data-series values to compute the maximal drawdown

    In order top optimize the code, I copied %matlabroot%/toolbox/finance/finance/maxdrawdown.m to a local folder on the Matlab path, renaming the file (and the function) maxdrawdn, in order to avoid conflict with the built-in version.

    Optimizing input args pre-processing

    The main problem with the pre-processing of the optional format input argument is the string comparisons, which are being done even when the default format is used (which is by far the most common case). String comparison are often more expensive than numerical computations. Each comparison by itself is very short, but when maxdrawdown is run in a loop (as it often is), the run-time adds up.

    Here’s a snippet of the original code:

    if nargin < 2 || isempty(Format)
        Format = 'return';
    end
    if ~ischar(Format) || size(Format,1) ~= 1
        error(message('finance:maxdrawdown:InvalidFormatType'));
    end
    choice = find(strncmpi(Format,{'return','arithmetic','geometric'},length(Format)));
    if isempty(choice)
        error(message('finance:maxdrawdown:InvalidFormatValue'));
    end

    An equivalent code, which avoids any string processing in the common default case, is faster, simpler and more readable:

    if nargin < 2 || isempty(Format)
        choice = 1;
    elseif ~ischar(Format) || size(Format,1) ~= 1
        error(message('finance:maxdrawdown:InvalidFormatType'));
    else
        choice = find(strncmpi(Format,{'return','arithmetic','geometric'},length(Format)));
        if isempty(choice)
            error(message('finance:maxdrawdown:InvalidFormatValue'));
        end
    end

    The general rule is that whenever you have a common case, you should check it first, avoiding unnecessary processing downstream. Moreover, for improved run-time performance (although not necessarily maintainability), it is generally preferable to work with numbers rather than strings (choice rather than Format, in our case).

    Vectorizing the main loop

    The main processing loop uses a very simple yet inefficient iterative loop. I assume that the code was originally developed this way in order to assist debugging and to ensure correctness, and that once it was ready nobody took the time to also optimize it for speed. It looks something like this:

    MaxDD = zeros(1,N);
    MaxDDIndex = ones(2,N);
    ...
    if choice == 1   % 'return' format
        MaxData = Data(1,:);
        MaxIndex = ones(1,N);
        for i = 1:T
            MaxData = max(MaxData, Data(i,:));
            q = MaxData == Data(i,:);
            MaxIndex(1,q) = i;
            DD = (MaxData - Data(i,:)) ./ MaxData;
            if any(DD > MaxDD)
                p = DD > MaxDD;
                MaxDD(p) = DD(p);
                MaxDDIndex(1,p) = MaxIndex(p);
                MaxDDIndex(2,p) = i;
            end
        end
    else             % 'arithmetic' or 'geometric' formats
        ...

    This loop can relatively easily be vectorized, making the code much faster, and arguably also simpler, more readable, and more maintainable:

    if choice == 3
        Data = log(Data);
    end
    MaxDDIndex = ones(2,N);
    MaxData = cummax(Data);
    MaxIndexes = find(MaxData==Data);
    DD = MaxData - Data;
    if choice == 1	% 'return' format
        DD = DD ./ MaxData;
    end
    MaxDD = cummax(DD);
    MaxIndex2 = find(MaxDD==DD,1,'last');
    MaxIndex1 = MaxIndexes(find(MaxIndexes<=MaxIndex2,1,'last'));
    MaxDDIndex(1,:) = MaxIndex1;
    MaxDDIndex(2,:) = MaxIndex2;
    MaxDD = MaxDD(end,:);

    Let’s make a short run-time and accuracy check – we can see that we achieved a 31-fold speedup (YMMV), and received the exact same results:

    >> data = rand(1,1e7);
     
    >> tic, [MaxDD1, MaxDDIndex1] = maxdrawdown(data); toc  % builtin Matlab function
    Elapsed time is 7.847140 seconds.
     
    >> tic, [MaxDD2, MaxDDIndex2] = maxdrawdn(data); toc  % our optimized version
    Elapsed time is 0.253130 seconds.
     
    >> speedup = round(7.847140 / 0.253130)
    speedup =
        31
     
    >> isequal(MaxDD1,MaxDD2) && isequal(MaxDDIndex1,MaxDDIndex2)  % check accuracy
    ans =
      logical
       1

    Disclaimer: The code above seems to work (quite well in fact) for a 1D data vector. You’d need to modify it a bit to handle 2D data – the returned maximal drawdown are still computed correctly but not the returned indices, due to their computation using the find function. This modification is left as an exercise for the reader…

    Very similar code could be used for the corresponding maxdrawup function. Although this function is used much less often than maxdrawdown, it is in fact widely used and very similar to maxdrawdown, so it is surprising that it is missing in the Financial Toolbox. Here is the corresponding code snippet:

    % Code snippet for maxdrawup (similar to maxdrawdn)
    MaxDUIndex = ones(2,N);
    MinData = cummin(Data);
    MinIndexes = find(MinData==Data);
    DU = Data - MinData;
    if choice == 1	% 'return' format
        DU = DU ./ MinData;
    end
    MaxDU = cummax(DU);
    MaxIndex = find(MaxDD==DD,1,'last');
    MinIndex = MinIndexes(find(MinIndexes<=MaxIndex,1,'last'));
    MaxDUIndex(1,:) = MinIndex;
    MaxDUIndex(2,:) = MaxIndex;
    MaxDU = MaxDU(end,:);

    Similar vectorization could be applied to the emaxdrawdown function. This too is left as an exercise for the reader…

    Conclusions

    Matlab is composed of thousands of internal functions. Each and every one of these functions was meticulously developed and tested by engineers, who are after all only human. Whereas supreme emphasis is always placed with Matlab functions on their accuracy, run-time performance sometimes takes a back-seat. Make no mistake about this: code accuracy is almost always more important than speed (an exception are cases where some accuracy may be sacrificed for improved run-time performance). So I’m not complaining about the current state of affairs.

    But when we run into a specific run-time problem in our Matlab program, we should not despair if we see that built-in functions cause slowdown. We can try to avoid calling those functions (for example, by reducing the number of invocations, or limiting the target accuracy, etc.), or optimize these functions in our own local copy, as I’ve shown last week and today. There are multiple techniques that we could employ to improve the run time. Just use the profiler and keep an open mind about alternative speed-up mechanisms, and you’d be half-way there.

    Let me know if you’d like me to assist with your Matlab project, either developing it from scratch or improving your existing code, or just training you in how to improve your Matlab code’s run-time/robustness/usability/appearance. I will be visiting Boston and New York in early June and would be happy to meet you in person to discuss your specific needs.

    ]]>
    https://undocumentedmatlab.com/blog/speeding-up-builtin-matlab-functions-part-2/feed 8
    Speeding-up builtin Matlab functions – part 1https://undocumentedmatlab.com/blog/speeding-up-builtin-matlab-functions-part-1 https://undocumentedmatlab.com/blog/speeding-up-builtin-matlab-functions-part-1#comments Sun, 29 Apr 2018 09:46:29 +0000 https://undocumentedmatlab.com/?p=7509
     
    Related posts:
    1. Speeding-up builtin Matlab functions – part 2 Built-in Matlab functions can often be profiled and optimized for improved run-time performance. This article shows a typical example. ...
    2. Solving a MATLAB bug by subclassing Matlab's Image Processing Toolbox's impoint function contains an annoying bug that can be fixed using some undocumented properties....
    3. Buggy Profiler option a bug exists in the profiler that prevents usage of its documented CPU timing functionality. ...
    4. Explicit multi-threading in Matlab part 4 Matlab performance can be improved by employing timer objects and spawning external processes. ...
     
    ]]>
    A client recently asked me to assist with his numeric analysis function – it took 45 minutes to run, which was unacceptable (5000 runs of ~0.55 secs each).Accelerating MATLAB Performance The code had to run in 10 minutes or less to be useful. It turns out that 99% of the run-time was taken up by Matlab’s built-in fitdist function (part of the Statistics Toolbox), which my client was certain is already optimized for maximal performance. He therefore assumed that to get the necessary speedup he must either switch to another programming language (C/Java/Python), and/or upgrade his computer hardware at considerable expense, since parallelization was not feasible in his specific case.

    Luckily, I was there to assist and was able to quickly speed-up the code down to 7 minutes, well below the required run-time. In today’s post I will show how I did this, which is relevant for a wide variety of other similar performance issues with Matlab. Many additional speed-up techniques can be found in other performance-related posts on this website, as well in my book Accelerating MATLAB Performance.

    Profiling

    The first step in speeding up any function is to profile its current run-time behavior using Matlab’s builtin Profiler tool, which can either be started from the Matlab Editor toolstrip (“Run and Time”) or via the profile function.

    The profile report for the client’s function showed that 99% of the time was spent in the Statistics Toolbox’s fitdist function. Drilling into this function in the profiling report, we see onion-like functions that processed input parameters, ensured data validation etc. The core processing is done inside a class that is unique to each required distribution (e.g., prob.StableDistribution, prob.BetaDistribution etc.) that is invoked within fitdist using an feval call, based on the distribution name that was specified by the user.

    In our specific case, the external onion layers of sanity checks were unnecessary and could be avoided. In general, I advise not to discard such checks, because you never know whether future uses might have a problem with outlier data or input parameters. Moreover, in the specific case of fitdist they take only a very minor portion of the run-time (this may be different in other cases, such as the ismember function that I described years ago, where the sanity checks have a significant run-time impact compared to the core processing in the internal ismembc function).

    However, since we wanted to significantly improve the total run-time and this was spent within the distribution class (prob.StableDistribution in the case of my client), we continue to drill-down into this class to determine what can be done.

    It turns out that prob.StableDistribution basically does 3 things in its main fit() method:

    1. pre-process the input data (prob.ToolboxFittableParametricDistribution.processFitArgs() and .removeCensoring() methods) – this turned out to be unnecessary in my client’s data, but has little run-time impact.
    2. call stablefit() in order to get fitting parameters – this took about half the run-time
    3. call stablelike() in order to get likelihood data – this took about half the run-time as well
    4. call prob.StableDistribution.makeFitted() to create a probability-distribution object returned to the caller – this also took little run-time that was not worth bothering about.

    The speed-up improvement process

    With user-created code we could simply modify our code in-place. However, a more careful process is necessary when modifying built-in Matlab functions (either in the core Matlab distribution or in one of the toolboxes).

    The basic idea here is to create a side-function that would replicate the core processing of fitdist. This is preferable to modifying Matlab’s installation files because we could then reuse the new function in multiple computers, without having to mess around in the Matlab installation (which may not even be possible if we do not have admin privileges). Also, if we ever upgrade our Matlab we won’t need to remember to update the installed files (and obviously retest).

    I called the new function fitdist2 and inside it I initially placed only the very core functionality of prob.StableDistribution.fit():

    % fitdist2 - fast replacement for fitdist(data,'stable')
    % equivalent to pd = prob.StableDistribution.fit(data);
    function pd = fitdist2(data)
        % Bypass pre-processing (not very important)
        [cens,freq,opt] = deal([]);
        %[data,cens,freq,opt] = prob.ToolboxFittableParametricDistribution.processFitArgs(data);
        %data = prob.ToolboxFittableParametricDistribution.removeCensoring(data,cens,freq,'stable');
     
        % Main processing in stablefit(), stablelike()
        params = stablefit(data,0.05,opt);
        [nll,cov] = stablelike(params,data);
     
        % Combine results into the returned probability distribution object
        pd = prob.StableDistribution.makeFitted(params,nll,cov,data,cens,freq);
    end

    If we try to run this as-is, we’d see errors because stablefit() and stablelike() are both sub-functions within %matlabroot%/toolbox/stats/stats/+prob/StableDistribution.m. So we copy these sub-functions (and their dependent subfunctions infoMtxCal(), intMle(), tabpdf(), neglog_pdf(), stable_nloglf(), varTrans) to the bottom of our fitdist2.m file, about 400 lines in total.

    We also remove places that call checkargs(…) since that seems to be unnecessary – if you want to keep it then add the checkargs() function as well.

    Now we re-run our code, after each speedup iteration verifying that the returned pd object returned by our fitdist2 is equivalent to the original object returned by fitdist.

    Speeding-up stablefit()

    A new profiling run shows that the vast majority of the time in stablefit() is spent in two lines:

    1. s = load('private/StablePdfTable.mat');
    2. [parmhat,~,err,output] = fminsearch(@(params)stable_nloglf(x,params),phi0,options);

    The first of these lines is reloading a static data file. The very same static data file is later reloaded in stablelike(). Both of these data-loads is done in every single invocation of fitdist, so if we have 5000 data fits, we load the same static data file 10,000 times! This is certainly not indicative of good programming practice. It would be much faster to reload the static data once into memory, and then use this cached data for the next 9,999 invocation. Since the original authors of StableDistribution.m seem to like single-character global variables (another bad programming practice, for multiple reasons), we’ll follow their example (added lines are highlighted):

    persistent s  % this should have a more meaningful name (but at least is not global...)!if isempty(s)    fit_path = fileparts(which('fitdist'));    s = load([fit_path '/private/StablePdfTable.mat']);
        a = s.a;
        b = s.b;
        xgd = s.xgd;
        p = s.p;
    end

    In order to speed-up the second line (that calls fminsearch), we can reduce the tolerances used by this function, by updating the options structure passed to it, so that we use tolerances of 1e-3 rather than the default 1e-6 (in our specific case this resulted in acceptable errors of ~0.1%). Specifically, we modify the code from this:

    function [parmhat,parmci] = stablefit(x,alpha,options)
    ...
    if nargin < 3 || isempty(options)
        options = statset('stablefit');
    else
        options = statset(statset('stablefit'),options);
    end
     
    % Maximize the log-likelihood with respect to the transformed parameters
    [parmhat,~,err,output] = fminsearch(@(params)stable_nloglf(x,params),phi0,options);
    ...
    end

    to this (note that the line that actually calls fminsearch remains unchanged):

    function [parmhat,parmci] = stablefit(x,alpha,unused_options)...
    persistent optionsif isempty(options)    options = statset('stablefit');
        options.TolX   = 1e-3;    options.TolFun = 1e-3;    options.TolBnd = 1e-3;end
     
    % Maximize the log-likelihood with respect to the transformed parameters
    [parmhat,~,err,output] = fminsearch(@(params)stable_nloglf(x,params),phi0,options);
    ...
    end

    The fminsearch internally calls tabpdf() repeatedly. Drilling down in the profiling report we see that it recomputes a griddedInterpolant object that is essentially the same for all iterations (and therefore a prime candidate for caching), and also that it uses the costly cubic interpolation rather than a more straight-forward linear interpolation:

    function y = tabpdf(x,alpha,beta)
    ...
    persistent G  % this should have a more meaningful name (but at least is not global...)!if isempty(G)    G = griddedInterpolant({b, a, xgd}, p, 'linear','none');  % 'linear' instead of 'cubic'end%G = griddedInterpolant({b, a, xgd}, p, 'cubic','none');  % original
    y = G({beta,alpha,x});
    ...

    These cases illustrate two important speedup technique categories: caching data in order to reduce the number of times that a certain code hotspot is being run, and modifying the parameters/inputs in order to reduce the individual run-time of the hotspot code. Variations of these techniques form the essence of effective speedup and can often be achieved by just reflecting on the problem and asking yourself two questions:

    1. can I reduce the number of times that this code is being run? and
    2. can I reduce the run-time of this specific code section?

    Additional important speed-up categories include parallelization, vectorization and algorithmic modification. These are sometimes more difficult programmatically and riskier in terms of functional equivalence, but may be required in case the two main techniques above are not feasible. Of course, we can always combine these techniques, we don’t need to choose just one or the other.

    Speeding-up stablelike()

    We now turn our attentions to stablelike(). As for the loaded static file, we could simply use the cached s to load the data in order to avoid repeated reloading of the data from disk. But it turns out that this data is actually not used at all inside the function (!) so we just comment-out the old code:

    %s = load('private/StablePdfTable.mat');
    %a = s.a;
    %b = s.b;
    %xgd = s.xgd;
    %p = s.p;

    Think about this – the builtin Matlab code loads a data file from disk and then does absolutely nothing with it – what a waste!

    Another important change is to reduce the run-time of the integral function, which is called thousands of times within a double loop. We do this by reducing the tolerances specified in the integral call from 1e-6:

    F(i,j) = integral(@(x)infoMtxCal(x,params,step,i,j),-Inf,Inf,'AbsTol',1e-6,'RelTol',1e-4); % original
    F(i,j) = integral(@(x)infoMtxCal(x,params,step,i,j),-Inf,Inf,'AbsTol',1e-3,'RelTol',1e-3); % new

    You can see that once again these two cases follow the two techniques that I mentioned above: we reduced the number of times that we load the data file (to 0 in our case), and we improved the run-time of the individual integral calculation by reducing its tolerances.

    Conclusions

    The final result of applying the techniques above was a 6-fold speedup, reducing the total run-time from 45 minutes down to 7 minutes. I could probably have improved the run-time even further, but since we reached our target run-time I stopped there. The point after all was to make the code usable, not to reach a world speed record.

    In my next article I will present another example of dramatically improving the run-time speed of a built-in Matlab function, this time a very commonly-used function in the Financial Toolbox that I was able to speed-up by a factor of 40.

    Matlab releases improve continuously, so hopefully my techniques above (or alternatives) would find their way into the builtin Matlab functions, making them faster than today, out-of-the-box.

    Until this happens, we should not lose hope when faced with a slow Matlab function, even if it is a built-in/internal one, as I hope to have clearly illustrated today, and will also show in my next article. Improving the performance is often easy. In fact, it took me much longer to write this article than it was to improve my client’s code…

    Let me know if you’d like me to assist with your Matlab project, either developing it from scratch or improving your existing code, or just training you in how to improve your Matlab code’s run-time/robustness/usability/appearance. I will be visiting Boston and New York in early June and would be happy to meet you in person to discuss your specific needs.

    ]]>
    https://undocumentedmatlab.com/blog/speeding-up-builtin-matlab-functions-part-1/feed 6
    Spicing up the Matlab Editorhttps://undocumentedmatlab.com/blog/spicing-up-the-matlab-editor https://undocumentedmatlab.com/blog/spicing-up-the-matlab-editor#respond Thu, 29 Mar 2018 21:34:20 +0000 https://undocumentedmatlab.com/?p=7472
     
    Related posts:
    1. Variables Editor scrolling The Matlab Variables Editor can be accessed to provide immediate scrolling to a specified cell location. ...
    2. EditorMacro – assign a keyboard macro in the Matlab editor EditorMacro is a new utility that enables setting keyboard macros in the Matlab editor. this post details its inner workings....
    3. Non-textual editor actions The UIINSPECT utility can be used to expand EditorMacro capabilities to non-text-insertion actions. This is how:...
    4. EditorMacro v2 – setting Command Window key-bindings The EditorMacro utility was extended to support built-in Matlab Editor and Command-Window actions and key-bindings. This post describes the changes and the implementation details....
     
    ]]>
    I’d like to introduce guest blogger Andreas Justin, who will discuss some way-cool features in his Editor Plugin utility. Many of his feature implementations are not Editor-specific and can be reused in other Matlab-Desktop applications, for example dockable panels, and integration with Matlab’s main Preferences window.

    Note: I will be traveling to the USA in June, and to Spain in August. If you would like me to visit your location for onsite consulting or training, then please let me know.

    Happy Easter/Passover!

    Overview

    Editor-plugin's cool logo

    Editor-plugin's cool logo

    Compared to other IDE like IntellIJ, Eclipse and many more, Matlab’s editor seems somewhat outdated. Especially writing Object-Oriented code in Matlab is kind of a hassle. To make Matlab more user friendly, I’ve written a Java app that adds important features to the editor – Features such as navigating inside Class-code and in Inherited members; Searching through methods and instantly jumping to desired location; Reopening an editor that was closed by accident; Storing bookmarks between Matlab sessions; and Live Templates using commands directly written in the editor, replaced by pre-defined text.

    The default Keyboard shortcuts listed below for the features can be customized. Most variables can be customized as well (I will point out which variables are not [yet] customizable).

    Most GUIs have a search field. Within this search field you can move the list or the tree up and down using arrow keys, or hit <escape> to return to editor. These search fields allow you to enter regular expressions to limit results shown in list or tree. Also, most GUIs are dockable.

    The Editor-Plugin utility is open-source. It is available on GitHub and also mirrored on the Matlab File Exchange. A detailed setup guide is provided on the utility’s wiki section in GitHub.

    If you discover any problem or have any suggestion for improvement, please visit the utility’s Issues section on GitHub, where all open/closed issues can be tracked and discussed.

    A brief overview of some of the features is presented below. For a detailed explanation of these and other features (which are not listed below), please review the Features section of the utility’s wiki (you guessed it: on GitHub…).

    Editing

    • Delete / duplicate linesCTRL + SHIFT + Y or D allows you to delete or duplicate current line.
      Deleting/duplicating complete lines
    • Move lines up or downCTRL + ALT + UP or DOWN allows you to move selected lines up or down.
      Moving complete lines
    • Live (auto-replace) templates – Live Templates are editor commands you can design to insert predefined code. Here’s an example for the command %this% (delivered within the package). When you type a command into the editor the string will get replaced by the predefined text. This predefined text may include variables depending on what you want to achieve. %this% was designed to insert the fully qualified name of the current class you’re in (or function, or script).
      Auto-replace template
    • Clipboard StackCTRL + SHIFT + V opens the Clipboard Stack, where the last 10 copied/cut text are stored and can be directly inserted into the current editor position. The Clipboard Stack only stores text copied from editor.

    • Auto switch current folder and detail viewer – Switching editor tabs will update the Desktop’s current folder and detail viewer. This behavior can be directly changed in the detail viewer bar.
      Use the new icons in the Desktop’s Current Folder panel: Toggle detail viewer to toggle detail viewer and Toggle following the current editor file to toggle following the current editor file.
      Follow current editor file
    • File Structure – I personally use this one the most: CTRL + F12 will show a GUI that let you search methods and properties including inherited ones.
      File structure analysis
    • Navigation History – Every editor file that is opened is stored in the navigation history. Up to 50 editor file paths are stored. If you have a 5-Button Mouse, you can navigate through previous location (backward and forward).
    • Recently ClosedCTRL + SHIFT + T will show a GUI that allows you to reopen closed editors this or last session.
    • BookmarksCTRL + SHIFT + F2 will show a GUI that allows you to name, search and delete bookmarks. Matlab has this nice feature to delete bookmarks after closing the editor. This feature will store all bookmarks. If an editor or Matlab is closed and opened later. All Bookmarks will be restored.
      This feature has some issues though. The most obvious first: if the source file has been changed outside of Matlab, the bookmark does not get updated, and may point to the wrong line, or gets deleted. Also on some Systems the default shortcut does not work. But there are workarounds.

    Other useful features

    • Dockable Windows – As mentioned before, most GUIs are dockable.
      Dockable Windows
    • Preferences – Preferences panel integrated in Matlab’s main Preferences window.
      Preferences panel integrated in Matlab's main Preferences window
    • Execute current lineSHIFT + F9 allows you to execute the current line. This is equivalent to selecting the entire line, then clicking F9, and is similar in concept to the editor’s built-in ability to execute the current cell-block.
      Execute current line
    • VarDiff – Select two variables in the workspace and compare them using Matlab’s internal comparison feature (adding a custom hook to the Workspace context-menu was discussed by Yair back in 2010).
      Variables comparison integrated in the Workspace browser
    • KeyPressListener – Allows you to create custom keyboard shortcuts for your own functions. A similar functionality was discussed by Yair back in 2009.

    As noted above, a detailed explanation of these and other features is provided in the Features section of the utility’s wiki. If you discover any problem or have any suggestion for improvement, please visit the utility’s Issues section on GitHub, where issues can be tracked and discussed.

    ]]>
    https://undocumentedmatlab.com/blog/spicing-up-the-matlab-editor/feed 0
    Auto-scale image colorshttps://undocumentedmatlab.com/blog/auto-scale-image-colors https://undocumentedmatlab.com/blog/auto-scale-image-colors#comments Wed, 21 Feb 2018 18:06:23 +0000 https://undocumentedmatlab.com/?p=7334
     
    Related posts:
    1. Introduction to UDD UDD classes underlie many of Matlab's handle-graphics objects and functionality. This article introduces these classes....
    2. Multi-column (grid) legend This article explains how to use undocumented axes listeners for implementing multi-column plot legends...
    3. UDD Events and Listeners UDD event listeners can be used to listen to property value changes and other important events of Matlab objects...
    4. Customizing axes part 3 – Backdrop Matlab HG2 axes can be customized in many different ways. This article explains some of the undocumented aspects. ...
     
    ]]>
    I deal extensively in image processing in one of my consulting projects. The images are such that most of the interesting features are found in the central portion of the image. However, the margins of the image contain z-values that, while not interesting from an operational point-of-view, cause the displayed image’s color-limits (axes CLim property) to go wild. An image is worth a thousand words, so check the following raw image (courtesy of Flightware, Inc.), displayed by the following simple script:

    hImage = imagesc(imageData); colormap(gray); colorbar;

    Raw image with default Matlab CLim

    Raw image with default Matlab CLim

    Rescaling the axes color-limits

    As you can see, this image is pretty useless for human-eye analysis. The reason is that while all of the interesting features in the central portion of the image have a z-value of ~-6, the few pixels in the margins that have a z-value of 350+ screw up the color limits and ruin the perceptual resolution (image contrast). We could of course start to guess (or histogram the z-values) to get the interesting color-limit range, and then manually set hAxes.CLim to get a much more usable image:

    hAxes = hImage.Parent; hAxes.CLim = [-7.5,-6];

    Raw image with a custom CLim

    Raw image with a custom CLim

    Auto-scaling the axes color-limits

    Since the z-values range and distribution changes between different images, it would be better to automatically scale the axes color-limits based on an analysis of the image. A very simple technique for doing this is to take the 5%,95% or 10%,90% percentiles of the data, clamping all outlier data pixels to the extreme colors. If you have the Stats Toolbox you can use the prctile function for this, but if not (or even if you do), here’s a very fast alternative that automatically scales the axes color limits based on the specified threshold (a fraction between 0-0.49):

    % Rescale axes CLim based on displayed image portion's CData
    function rescaleAxesClim(hImage, threshold)
        % Get the displayed image portion's CData
        CData = hImage.CData;
        hAxes = hImage.Parent;
        XLim = fix(hAxes.XLim);
        YLim = fix(hAxes.YLim);
        rows = min(max(min(YLim):max(YLim),1),size(CData,1)); % visible portion
        cols = min(max(min(XLim):max(XLim),1),size(CData,2)); % visible portion
        CData = CData(unique(rows),unique(cols));
        CData = CData(:);  % it's easier to work with a 1d array
     
        % Find the CLims from this displayed portion's CData
        CData = sort(CData(~isnan(CData)));  % or use the Stat Toolbox's prctile()
        thresholdVals = [threshold, 1-threshold];
        thresholdIdxs = fix(numel(CData) .* thresholdVals);
        CLim = CData(thresholdIdxs);
     
        % Update the axes
        hAxes.CLim = CLim;
    end

    Note that a threshold of 0 uses the full color range, resulting in no CLim rescaling at all. At the other extreme, a threshold approaching 0.5 reduces the color-range to a single value, basically reducing the image to an unusable B/W (rather than grayscale) image. Different images might require different thresholds for optimal contrast. I believe that a good starting point for the threshold is a value of 0.10, which corresponds to the 10-90% range of CData values.

    Dynamic auto-scaling of axes color-limits

    This is very nice for the initial image display, but if we zoom-in, or pan a sub-image around, or update the image in some way, we would need to repeat calling this rescaleAxesClim() function every time the displayed image portion changes, otherwise we might still get unusable images. For example, if we zoom into the image above, we will see that the color-limits that were useful for the full image are much less useful on the local sub-image scale. The first (left) image uses the static custom color limits [-7.5,-6] above (i.e., simply zooming-in on that image, without modifying CLim again); the second (right) image is the result of repeating the call to rescaleAxesClim(), which improves the image contrast:

    Zoomed-in image with a custom static CLim

    Zoomed-in image with a custom static CLim

    Zoomed-in image with a re-applied custom CLim

    Zoomed-in image with a re-applied custom CLim

    We could in theory attach the rescaleAxesClim() function as a callback to the zoom and pan functions (that provide such callback hooks). However, we would still need to remember to manually call this function whenever we modify the image or its containing axes programmatically.

    A much simpler way is to attach our rescaleAxesClim() function as a callback to the image’s undocumented MarkedClean event:

    % Instrument image: add a listener callback to rescale upon any image update
    addlistener(hImage, 'MarkedClean', @(h,e)rescaleAxesClim(hImage,threshold));

    In order to avoid callback recursion (potentially caused by modifying the axes CLim within the callback), we need to add a bit of code to the callback that prevents recursion/reentrancy (details). Here’s one simple way to do this:

    % Rescale axes CLim based on displayed image portion's CData
    function rescaleAxesClim(hImage, threshold)
        % Check for callback reentrancy
        inCallback = getappdata(hImage, 'inCallback');
        if ~isempty(inCallback), return, end
        try
            setappdata(hImage, 'inCallback',1);  % prevent reentrancy
     
            % Get the displayed image portion's CData
            ...  (copied from above)
     
            % Update the axes
            hAx.CLim = CLim;
            drawnow; pause(0.001);  % finish all graphic updates before proceeding
        catch
        end
        setappdata(hImage, 'inCallback',[]);  % reenable this callback
    end

    The result of this dynamic automatic color-scaling can be seen below:

    Zoomed-in image with dynamic CLim

    Zoomed-in image with dynamic CLim

    autoScaleImageCLim utility

    I have created a small utility called autoScaleImageCLim, which includes all the above, and automatically sets the specified input image(s) to use auto color scaling. Feel free to download this utility from the Matlab File Exchange. Here are a few usage examples:

    autoScaleImageCLim()           % auto-scale the current axes' image
    autoScaleImageCLim(hImage,5)   % auto-scale image using 5%-95% CData limits
    autoScaleImageCLim(hImage,.07) % auto-scale image using 7%-93% CData limits

    (note that the hImage input parameter can be an array of image handles)

    Hopefully one day the so-useful MarkedClean event will become a documented and fully-supported event for all HG objects in Matlab, so that we won’t need to worry that it might not be supported by some future Matlab release…

    ]]>
    https://undocumentedmatlab.com/blog/auto-scale-image-colors/feed 1
    Adding custom properties to GUI objectshttps://undocumentedmatlab.com/blog/adding-custom-properties-to-gui-objects https://undocumentedmatlab.com/blog/adding-custom-properties-to-gui-objects#comments Thu, 15 Feb 2018 12:39:35 +0000 https://undocumentedmatlab.com/?p=7324
     
    Related posts:
    1. Matlab and the Event Dispatch Thread (EDT) The Java Swing Event Dispatch Thread (EDT) is very important for Matlab GUI timings. This article explains the potential pitfalls and their avoidance using undocumented Matlab functionality....
    2. Tab panels – uitab and relatives This article describes several undocumented Matlab functions that support tab-panels...
    3. uitree This article describes the undocumented Matlab uitree function, which displays data in a GUI tree component...
    4. Borderless button used for plot properties A borderless button can be used to add unobtrusive functionality to plot axes...
     
    ]]>
    Matlab objects have numerous built-in properties (some of them publicly-accessible/documented and others not, but that’s a different story). For various purposes, it is sometimes useful to attach custom user-defined properties to such objects. While there was never a fully-documented way to do this, most users simply attached such properties as fields in the UserData property or the object’s [hidden] ApplicationData property (accessible via the documented setappdata/getappdata functions).

    An undocumented way to attach actual new user-defined properties to objects such as GUI handles or Java references has historically (in HG1, up to R2014a) been to use the undocumented schema.prop function, as I explained here. As I wrote in that post, in HG2 (R2014b onward), we can use the fully-documented addprop function to add new custom properties (and methods) to such objects. What is still NOT documented, as far as I could tell, is that all of Matlab’s builtin handle graphics objects indirectly inherit the dynamicprops class, which allows this. The bottom line is that we can dynamically add custom properties in run-time to any HG object, without affecting any other object. In other words, the new properties will only be added to the handles that we specifically request, and not to any others.

    All this is important, because for some unexplained reason that escapes my understanding, MathWorks chose to seal its classes, thus preventing users to extend them with sub-classes that contain the new properties. So much frustration could have been solved if MathWorks would simply remove the Sealed class meta-property from its classes. Then again, I’d have less to blog about in that case…

    Anyway, why am I rehashing old news that I have already reported a few years ago?

    Well, first, because my experience has been that this little tidbit is [still] fairly unknown by Matlab developers. Secondly, I happened to run into a perfect usage example a short while ago that called for this solution: a StackExchange user asked whether it is possible to tell a GUI figure’s age, in other words the elapsed time since the figure was created. The simple answer would be to use setappdata with the creation date whenever we create a figure. However, a “cleaner” approach seems to be to create new read-only properties for the figure’s CreationTime and Age:

    First, create a small Matlab function as follows, that attaches the CreationTime property to a figure:

    function setCreationTime(hFig,varargin)
       hProp = addprop(hFig,'CreationTime');
       hFig.CreationTime = now;
       hProp.SetAccess = 'private';  % make property read-only after setting its initial value
     
       hProp = addprop(hFig,'Age');
       hProp.GetMethod = @(h,e) etime(datevec(hFig.CreationTime), clock);  % compute on-the-fly
       hProp.SetAccess = 'private';  % make property read-only
    end

    Now assign this function as the default CreateFcn callback function for all new figures from now on:

    set(0,'DefaultFigureCreateFcn',@setCreationTime)

    That’s it – you’re done! Whenever a new figure will be created from now on, it will have two custom read-only properties: CreationTime and Age.

    For example:

    >> newFig = figure;
    >> newFig.CreationTime
    ans =
          737096.613706748
     
    >> ageInDays = now - newFig.CreationTime
    ageInDays = 
           0.0162507836846635
    >> ageDuration = duration(ageInDays*24,0,0)
    ageDuration = 
      duration
       00:23:24
    >> ageString = datestr(ageInDays, 'HH:MM:SS.FFF')
    ageString = 
        '00:23:24.068'
     
    >> ageInSecs = newFig.Age
    ageInSecs =
           1404.06771035492

    Note that an alternative way to set the computed property Age would have been to set its value to be an anonymous function, but this would have necessitated invoking it with parenthesis (as in: ageInSecs = newFig.Age()). By setting the property’s GetMethod meta-property we avoid this need.

    Keen readers will have noticed that the mechanism that I outlined above for the Age property/method can also be used to add custom user methods. For example, we can create a new custom property named refresh that would be read-only and have a GetMethod which is the function handle of the function that refreshes the object in some way.

    Do you have any special uses for custom user-defined properties/methods in your program? or perhaps you have a use-case that might show MathWorks why sub-classing the built-in classes might improve your work? if so, then please place a comment about it below. If enough users show MathWorks why this is important, then maybe it will be fixed in some future release.

    ]]>
    https://undocumentedmatlab.com/blog/adding-custom-properties-to-gui-objects/feed 5