Posts Tagged ‘pure Matlab’

Setting desktop tab completions

Wednesday, March 3rd, 2010

This site has lately focused on quite detailed Java-related topics. Next week I will present the promised EDT article, which will dive into even deeper Java territory. So I thought to take a short break and present an entirely pure-Matlab non-Java undocumented feature, which is simple and yet quite useful.

A few months ago, a CSSM reader asked whether it is possible to customize Matlab tab-completion for user-defined functions (see related). A similar question on StackOverflow provided the necessary solution lead:

Apparently, Matlab has a file called TC.xml in its [matlabroot '/toolbox/local/'] folder that contains the definitions of the tab-completable functions and their arguments. In order for a user-defined function’s arguments to support tab-completion, a new entry needs to be added to this XML file.

TC.xml & TC.xsd

The full syntax of the TC.xml file can be found in the TC.xsd file, which is located in the same folder as TC.xml. Here are some sample definitions from my TC.xml file (which might vary across Matlab releases):

<binding name="addpath" ctype="DIR"/>
<binding name="help"    ctype="FUN SUBFUN"/>
<binding name="clear"   ctype="FUN VAR"/>
 
<binding name="whos"    ctype="VAR">
  <arg previous="-file" ctype= "MATFILE"/>
</binding>
 
<binding name="open">
  <arg argn="1" ctype="VAR MATFILE FIGFILE MFILE MDLFILE FILE"/>
</binding>
 
<binding name="openfig">
  <arg argn="1" ctype="FIGFILE"/>
  <arg argn="2" ctype="VAR" value="new visible invisible reuse"/>
</binding>
 
<binding name="mlint"   ctype="FUN">
  <arg argn="2:10" ctype="VAR" value="-struct -string -id"/>
</binding>

The first example defines that an unlimited number of addpath arguments are all of type DIR. Therefore, when completing any argument of this function in the Command-Window, Matlab will present only relevant DIR (=folder) elements in the pop-up window (lexically sorted):

Tab-completion of type DIR

Tab-completion of type DIR

Similarly, help defines all its arguments to be a function or sub-function type, so the popup-up will only be populated with the function names currently visible in the desktop:

Tab-completion of types FUN & SUBFUN

Tab-completion of types FUN & SUBFUN

Similarly, clear defines all its arguments as function names or variables. Note that the list of available functions and variables may change depending on the current execution stack position. The full list of supported types is defined in the TC.xsd file. It is: VAR, FUN, SUBFUN, DIR, FILE, MFILE, MATFILE, FIGFILE and MDLFILE.

The whos function defines all its arguments as VAR, except the single MATFILE argument that follows a ‘-file’ argument (look at whos’s help page to understand why).

The open function defines tab completion only for its first argument (with plenty of possible types…). Likewise, openfig defines its first argument as a FIGFILE, and its second as VAR with a few extra special-purpose strings that are added to the popup-up menu.

Finally, the mlint example shows that multiple arguments can be defined using a single XML definition element. In this case, args #2-10 are defined as VAR (with three extra special-purpose strings), while arg #1 and 11+ are defined as FUN.

The careful user can edit the TC.xml file using any text editor (I strongly suggest saving a backup first):

edit(fullfile(matlabroot,'toolbox/local/TC.xml'))

User-defined functions can easily be added to TC.xml, and we can even add/modify the built-in Matlab functions that are already defined. Note that changes to TC.xml only take effect after a Matlab restart. From then on, all future Matlab sessions will use the modification, so a really simple one-time edit can improve our workflow for a long time - at least until we upgrade Matlab, when we’ll need to redo our edits…

TabComplete utility

In order to facilitate TC.xml editing, I have created a utility called TabComplete, which is now available on the Matlab File Exchange. The use of this utility is very simple. For example:

tabcomplete test file 'DIR +data -data nodata' VAR

defines a user-defined function test that accepts a FILE argument, followed by a DIR argument with three special-purpose strings, followed by any number of VAR arguments. If I wished to define specific argument types without any default type, I would use:

tabcomplete test file 'DIR +data -data nodata' ''

Using TabComplete for user-defined functions

Using TabComplete for user-defined functions

TabComplete can also be used to retrieve the current list of tab-completion definitions:

>> definitions = tabcomplete;
>> definitions(1)
ans = 
    functionName: 'addpath'
     defaultType: 'DIR'
     extraValues: ''
        platform: ''
    functionArgs: []
 
>> definitions(54)
ans = 
    functionName: 'openfig'
     defaultType: ''
     extraValues: ''
        platform: ''
    functionArgs: [1x2 struct]
>> definitions(54).functionArgs(1)
ans = 
    previousArg: ''
        argType: 'FIGFILE'
    extraValues: ''
>> definitions(54).functionArgs(2)
ans = 
    previousArg: ''
        argType: 'VAR'
    extraValues: 'new visible invisible reuse'

TabComplete has a few limitations: it does not support the -previous option described above (you can do this by manually editing TC.xml). There are also some inherent limitations in Matlab’s TC functionality: changes take effect only after a Matlab restart (there might be a way to reload the definitions in the current Matlab session, but I do not know of any); the list of standard types cannot be modified; and the default type does not support extra special-purpose strings as do the numbered arguments.

There is another very annoying limitation: by default, TC.xml only supports lowercase function names. This is stupid, since Matlab has many function names with UPPERCASE characters, and certainly user-defined function names also do. Luckily, this last limitation can easily be overcome by editing the TC.xsd file (note that this is the TC.XSD file, not the TC.XML file). Instead of:

<xsd:simpleType name="tcBindingNameType">
  <xsd:restriction base="xsd:token">
    <xsd:pattern value='[A-Za-z_0-9]+(/[a-z_0-9]+)?'/>
  </xsd:restriction>
</xsd:simpleType>

Change the xsd:pattern definition element to:

    <!-- Yair 21/2/2010: added A-Z -->
    <xsd:pattern value='[A-Za-z_0-9]+(/[A-Za-z_0-9]+)?'/>

(note the way comments can be added to the XSD/XML files)

P.S. an entirely different customization, for user-defined class members, was presented by Michal Kutil.

Bookmark and Share

Solving a MATLAB bug by subclassing

Sunday, February 7th, 2010

I would like to welcome guest blogger Matthew Whitaker. Many of Matt’s CSSM submissions offer important insight of internal Matlab functionality. As shall be seen by today’s article and some future submissions, Matt has plenty to share vis-a-vis Matlab’s undocumented functionality.

In my day-to-day work I make extensive use of MATLAB’s Image Processing Toolbox (IPT). One area of the toolbox that has seen considerable change over the last few releases has been the development of a set of modular tools to aid in GUI design for image processing applications. In this article, I examine a bug in one of those tools to illustrate how we can use the power of subclassing these objects (using an undocumented property) to design a simple and effective workaround.

The problem

The problem arose as I was refactoring some code that was written in R2006b to R2009b. The code in question uses the impoint tool on an image along with an associated text object that moves with the point to display information as it is dragged around the image. At the time of the R2006b release the impoint tool was written as an API. In R2006b the call to impoint returns a handle to an hggroup containing a structure of function handles in its application data under the tag ‘API’. This programming pattern was common before the advent of the new class syntax in MATLAB version 7.6 (R2008a).

Here is an example of how impoint would be used in R2006b:

function impointBehavior_R2006b
%IMPOINTBEHAVIOR_R2006B shows how impoint would be used in R2006b
%Note: RUN UNDER R2006B (will run under R2009b but actually uses
%classdef impoint so it will show the same issue)
 
  % Display the image in a figure window
  figure;  imshow('rice.png');
 
  % In R2006b calling impoint returns the hggroup handle
  h = impoint(gca,100,200);
 
  % In 2006b iptgetapi returns a structure of function handles
  api = iptgetapi(h);
 
  % Add a new position callback to set the text string
  api.addNewPositionCallback(@newPos_Callback);
 
  % Construct boundary constraint function so we can't go outside the axes
  fcn = makeConstrainToRectFcn('impoint',get(gca,'XLim'),get(gca,'YLim'));
  api.setDragConstraintFcn(fcn);
 
  % Fire callback so we get initial text
  newPos_Callback(api.getPosition());
 
  function newPos_Callback(newPos)
    % Display the current point position in a text label
    api.setString(sprintf('(%1.0f,%1.0f)',newPos(1),newPos(2)));
  end %newPos_Callback
 
end %impointBehavior_R2006b

The code above, when run in R2006b, produces the desired behavior of displaying a text object containing the point coordinates that moves around with the point as it is dragged around the axes.

In R2009b, impoint is now a true MATLAB class using the new classdef syntax, so I wanted to update the existing code. Initially this appeared to be a straightforward translation of the code to make use of the new impoint class syntax. The first attempt to rewrite the code was:

function impointBehavior_R2009b
%IMPOINTBEHAVIOR_R2009B shows the undesirable behavior when
%using the setString method in R2009b. 
 
  % Display the image in a figure window
  figure;  imshow('rice.png');
  h = impoint(gca,100,200);
 
  % Add a new position callback to set the text string
  h.addNewPositionCallback(@newPos_Callback);
 
  % Construct boundary constraint function so we can't go outside the axes
  fcn = makeConstrainToRectFcn('impoint',get(gca,'XLim'),get(gca,'YLim'));
 
  % Enforce boundary constraint function
  h.setPositionConstraintFcn(fcn);
 
  % Fire callback so we get initial text
  newPos_Callback(h.getPosition());
 
  function newPos_Callback(newPos)
    % Display the current point position in a text label
    h.setString(sprintf('(%1.0f,%1.0f)',newPos(1),newPos(2)))
  end %newPos_Callback
 
end %impointBehavior_R2009b

Unfortunately, when this code is run, dragging the mouse around the axes produces a trail of labels as shown below:

Before - buggy behavior

Before - buggy behavior

Opening up impoint.m in the editor and tracing the code revealed that impoint’s setString method creates a new text object each time it is used. I reported this to MATLAB and the bug is now documented on the MathWorks Support site (574846).

The solution

So how do we work around this bug to get to the behavior we want? One solution would be to rewrite the offending MATLAB code but this is somewhat risky in terms of maintainability and compatibility.

A more elegant solution is to subclass the impoint class and substitute the setString behavior we want. Looking at the impoint code we find that impoint is a subclass of imroi. In the imroi property declarations we see a number of undocumented properties that are protected. We can access these properties in a subclass but not outside the class. One of these undocumented properties is h_group which is an hggroup that contains the handle graphic objects that make up the impoint on the screen. The label, when created, becomes part of this hggroup with its Tag property set to ‘label’. When performing the setString method the behavior we want to see is that if the text object exists we want to update its String property. If it does not exist we want it to perform its existing functionality:

classdef impointtextupdate < impoint
%IMPOINTTEXTUPDATE subclasses impoint to override the setString
%method of impoint so that it does not create a new text object
%each time it is called.   
 
  methods
    function obj = impointtextupdate(varargin)
      obj = obj@impoint(varargin{:});
    end %impointtextupdate
 
    function setString(obj,str)
      %override impoint setString
 
      %check to see if there is already a text label
      label = findobj(obj.h_group,'Type','text','Tag','label');
 
      if isempty(label)
        %do the default behavior
        setString@impoint(obj,str);
      else
        %update the existing tag
        set(label(1),'String',str);
      end %if
    end %setString
 
  end %methods
end %impointtextupdate

Substituting calls to impoint with the new impointupdatetext subclass now produces the desired effect as shown below:

After - expected behavior

After - expected behavior

Conclusions

This case illustrates a couple of points:

  • Much of the existing code in the MATLAB toolboxes is being updated to the new object oriented syntax. This presents many opportunities to easily and elegantly modify the default behavior without modifying provided toolbox code In the example above we retain all the desirable behavior of impoint while overriding the undesirable behavior.
  • Many of the properties and methods in the provided toolbox objects are hidden or protected and are undocumented. It takes some simple detective work to find these out through examining the code. MATLAB is very generous in providing much of the existing code openly. Open the functions and classes you use in the editor to really find out how they work. Over the years I’ve learned and adopted a lot of useful MATLAB programming patterns by examining the code in the various toolboxes (there are a few coding horrors as well!).

I hope to explore some other under-documented features of the IPT and other toolboxes in future posts.

Bookmark and Share

GUI integrated browser control

Wednesday, December 9th, 2009

Last week, I described the built-in PopupPanel object, and showed how it can be used to present popup messages with HTML content and even entire webpages. I explained that PopupPanel uses an internal browser object to achieve this. In fact, Matlab’s browser object predates PopupPanel by many years and quite a few releases. This browser object can be used as a stand-alone component that we can easily embed in our Matlab GUI applications.

Here is a simple example in which a Matlab Listbox uicontrol is used to select the contents of an adjacent browser component:

% Create a blank figure window
f=figure('Name','Browser GUI demo','Num','off','Units','norm');
 
% Add the browser object on the right
jObject = com.mathworks.mlwidgets.html.HTMLBrowserPanel;
[browser,container] = javacomponent(jObject, [], f);
set(container, 'Units','norm', 'Pos',[0.3,0.05,0.65,0.9]);
 
% Add the URLs listbox on the left
urls = {'www.cnn.com','www.bbc.co.uk','myLocalwebpage.html',...
        'www.Mathworks.com', 'UndocumentedMatlab.com'};
hListbox = uicontrol('style','listbox', 'string',urls, ...
        'units','norm', 'pos',[0.05,0.05,0.2,0.9], ...
        'userdata',browser);
 
% Set the listbox's callback to update the browser contents
cbStr=['strs = get(gcbo,''string''); ' ...
      'url = strs{get(gcbo,''value'')};' ...
      'browser = get(gcbo,''userdata''); ' ...
      'msg=[''<html><h2>Loading '' url '' - please wait''];'...
      'browser.setHtmlText(msg); pause(0.1); drawnow;'...
      'browser.setCurrentLocation(url);'];
set(hListbox,'Callback',cbStr);

Browser object integrated in Matlab GUI (click for large image)

Browser object integrated in Matlab GUI

In this simple example, we can see how the Java browser object can easily be controlled by Matlab. Specifically, we use two modes of the browser: first we present an HTML message (’Loading www.cnn.com - please wait‘) and then replacing this content with the actual webpage, if accessible. If the webpage is not accessible, an error message is displayed:

Browser message when webpage is missing (click for large image)

Browser message when webpage is missing

We can easily expand this simple example to display any HTML message or webpage, in a seamless integration within our GUI.

Now, who ever said that Matlab GUI looks static or boring???

In an unrelated note, I would like to extend good wishes to Ken Orr, who has left the Mathworks Desktop development team to join Apple a few days ago. You probably know Ken from his good work on the Desktop and the official Matlab Desktop blog. Hopefully, in his new position Ken will be able to influence Mac Java in a way that will reduce the numerous recurring issues that afflict Matlab Mac releases.

Bookmark and Share

Customizing print setup

Wednesday, November 25th, 2009

Last week, while consulting a client, I watched him use a Matlab application that processed data and presented the results in a figure window. I saw that he constantly needed to go to File/Print-Preview menu option to customize the figure’s print setup before being able to print. He would constantly click the “Fill Page” button, then go to the Color tab and set RGB rather than Black-and-White, and finally go to the Advanced tab to prevent printing UI controls. Only then would he actually print the figure using the menu (File/Print) or the toolbar Print button.

Print Preview window

Print Preview window

This is absurd, I thought to myself - there must be a better way. Unfortunately, Matlab only supports two documented ways to modify the print setup:

  • the print function enables setting print settings when actually printing the figure. It accepts a long list of optional parameters that specify a wide range of printing options. But if I want to allow users to print at their own time, using the default figure menu/toolbar, I need to separate the actions of (1) setting the print setup and (2) doing the actual printout action. Since print does both of these together, we can’t use it. Moreover, if we don’t call print, the setup remains at the default settings and the menu/toolbar printouts use this default setup.
  • the printopt.m file stores the user’s default print setup. This file can be modified (use the printopt function). However, it affects all Matlab printouts, and cannot be configured on a figure-by-figure basis.

Of course, lack of a documented method never stopped us before. Sure enough, after a short search I discovered the hidden potential of the undocumented/unsupported setprinttemplate function. This function, called internally by Matlab’s print-related functions, is responsible for setting the figure’s initial print setup. Once I had this key, unlocking the problem was easy. Here is the bottom line:

The figure’s default print setup is stored in the figure’s hidden ApplicationData property, accessible via the getappdata and setappdata functions or directly via get/set, as explained in a previous post. More importantly, the figure-specific setup is stored in another hidden property, PrintTemplate. Both of these setup data are stored in structure format, which is not available when a figure is first created, but only after printing or print-preview. Note that printtemplate.m contains detailed explanations about the meaning of some fields - to see them, simply display the file:

>> type printtemplate.m  % or: edit printtemplate.m

Unless we set PrintTemplate ourselves (or call print or printpreview to do this), printing the figure will use the default print setup. To set a non-standard setup, we just need to create the PrintTemplate structure with our non-default setup options. Matlab will then automatically use them when printing the figure. Here is a checklist for doing so:

  1. First, create a figure and get its default print setup data. Since this data is unavailable in newly-created figures, simply open the figure’s Print-Preview window without changing anything - this will create the missing print setup structure that we can retrieve:

    % Create a simple figure
    >> hFig = figure;  surf(peaks);
     
    % Open the print-preview window to create setup data
    >> printpreview(hFig);
     
    % Retrieve the original (default) print setup data
    >> oldSetup = get(hFig,'PrintTemplate')
    oldSetup = 
           VersionNumber: 2
                    Name: ''
               FrameName: ''
             DriverColor: 1
         AxesFreezeTicks: 0
               tickState: {}
        AxesFreezeLimits: 0
                limState: {}
                   Loose: 0
                    CMYK: 0
                  Append: 0
               Adobecset: 0
                 PrintUI: 1
                Renderer: 'auto'
          ResolutionMode: 'auto'
                     DPI: 0
                FileName: 'untitled'
             Destination: 'printer'
             PrintDriver: ''
               DebugMode: 0
              StyleSheet: 'default'
                FontName: ''
                FontSize: 0
            FontSizeType: 'screen'
               FontAngle: ''
              FontWeight: ''
               FontColor: ''
               LineWidth: 0
           LineWidthType: 'screen'
            LineMinWidth: 0
               LineStyle: ''
               LineColor: ''
            PrintActiveX: 0
               GrayScale: 0
                 BkColor: 'white'
                 FigSize: [14.8054083333333 11.10405625]
  2. Next, go to the File/Print-Preview menu option and modify the setup according to your specific needs, and retrieve the new (modified setup):
    >> newSetup = get(hFig,'PrintTemplate');
  3. Now compare the two structures and retrieve only the modified setup options. This can be done in several ways - I personally use the objdiff utility. In our case, we modified the DriverColor (B&W => color), FigSize (for “Fill page”), and PrintUI (for hiding UI controls) fields:
    >> objdiff(oldSetup,newSetup)
    ans = 
        DriverColor: {[0]  [1]}
            FigSize: {[14.8054083333333 11.10405625]  [2x1 double]}
            PrintUI: {[1]  [0]}
         StyleSheet: {'default'  'modified'}
           limState: {{}  ''}
          tickState: {{}  ''}
  4. Finally, use the undocumented printtemplate and setprinttemplate functions to prepare the default setup sub-structure, and override with the modified options that you have just discovered. Place this in the figure’s _OpeningFcn function (for GUIDE-generated figures) or in your figure’s initialization function (for non-GUIDE figures). For example, if we have a GUIDE-generated figure called “MyFig”, then place this code in the MyFig_OpeningFcn function in MyFig.m:
    function MyFig_OpeningFcn(hObject, eventdata, handles, varargin)
      ...
      % This was adapted from initprintexporttemplate.m
      pt = printtemplate;
      pt.StyleSheet = 'modified';
      pt.VersionNumber = 2;   % important (Note #1 below)
      pt.FigSize = [38.1, 21.0];
      pt.DriverColor = 1;
      pt.PrintUI = 0;
      % we must set the paper size *before* setprinttemplate
      set(hObject, 'PaperPositionMode','manual', ...
                   'PaperPosition',[0 0.5 29.5 20], ...
                   'PaperSize',[29.68 20.98], ...
                   'PaperType','A4');
      setprinttemplate(hObject, pt);
     
      % Choose default command line output for MyFig
      handles.output = hObject;
     
      % Update handles structure
      guidata(hObject, handles);

That’s all there is to it. So easy once we know how, isn’t it? The most annoying pain-in-the-so-and-so sometimes have simple solutions…

Note #1: it is very important to set pt.VersionNumber to 2, otherwise some modifications will not take effect.

Note #2: the internal implementation of printtemplate as well as the internal setup fields have changed between Matlab releases. These were often minor backward-compatible changes, but at least once this was a major change (VersionNumber 1=>2, I think around Matlab 7.2, but I’m not sure). Therefore, carefully test your code on the oldest release which is supposed to run it. Also, if you plan the code to run in future Matlab releases, you should note that the entire setup functionality might break without prior notice, since it is an internal unsupported implementation.

Bookmark and Share

Customizing uiundo

Wednesday, November 4th, 2009

Last week I discussed uiundo - Matlab’s undocumented undo/redo manager. Today, I will show how this object can be customized for some specific needs. However, we first need to understand a little more about how uiundo works beneath the hood.

Matlab stores all of a figure’s undo/redo data in a hidden figure object, referenced by getappdata(hFig,’uitools_FigureToolManager’). This means that by default uiundo works at the figure level, rather than the application level or the GUI component level. If we wish to modify this default behavior, we need to programmatically inspect and filter the undo/redo actions stack based on the action source. Read below to see how this can be done.

The hidden uitools_FigureToolManager object, defined in %MATLABROOT%\toolbox\matlab\uitools\@uiundo\, uses a stack to store instances of the undo/redo cmd data structure introduced in last week’s post:

% Retrieve redo/undo object
undoObj = getappdata(hFig,'uitools_FigureToolManager');
if isempty(undoObj)
   undoObj = uitools.FigureToolManager(hFig);
   setappdata(hFig,'uitools_FigureToolManager',undoObj);
end
 
>> get(undoObj)
    CommandManager: [1x1 uiundo.CommandManager]
            Figure: [1x1 figure]
        UndoUIMenu: [1x1 uimenu]
        RedoUIMenu: [1x1 uimenu]

There are several interesting things we can do with this undoObj. First, let’s modify the main-menu items (I will discuss menu customization in more detail in another post):

% Modify the main menu item (similarly for redo/undo)
if ~isempty(undoObj.RedoUIMenu)
   undoObj.RedoUIMenu.Position =1; %default=2 (undo above redo)
   undoObj.RedoUIMenu.Enable = 'off';     % default='on'
   undoObj.RedoUIMenu.Checked = 'on';     % default='off'
   undoObj.RedoUIMenu.ForegroundColor = [1,0,0];  % =red
end
if ~isempty(undoObj.UndoUIMenu)
   undoObj.UndoUIMenu.Label = '<html><b><i>Undo action';
   % Note: &Undo underlines 'U' and adds a keyboard accelerator
   % but unfortunately only if the label is non-HTML ...
   undoObj.UndoUIMenu.Separator = 'on';   % default='off'
   undoObj.UndoUIMenu.Checked = 'on';     % default='off'
   undoObj.UndoUIMenu.ForegroundColor = 'blue'; % default=black
end

Menu before customization

Menu after customization

Figure menu before and after customization

Now, let’s take a look at undoObj’s CommandManager child (the Figure child object is simply handle(hFig), and so is not very interesting):

>> undoObj.CommandManager.get
             UndoStack: [13x1 uiundo.FunctionCommand]
             RedoStack: [1x1 uiundo.FunctionCommand]
    MaxUndoStackLength: []
               Verbose: []
 
>> undoObj.CommandManager.UndoStack(end).get
             Parent: []
       MCodeComment: []
               Name: 'slider update (0.48 to 0.38)'
           Function: @internal_update
           Varargin: {[53.0037841796875]  [0.38]  [1x1 double]}
    InverseFunction: @internal_update
    InverseVarargin: {[53.0037841796875]  [0.48]  [1x1 double]}

This looks familiar: In fact, it is exactly the cmd data structure being passed to the uiundo function, with the additional (apparently unused) properties Parent and MCodeComment. CommandManager’s UndoStack and RedoStack child objects contain all stored undo/redo actions such that the latest action is at the end of these arrays. In the snippet above, there are 13 undo-able actions, with the latest action in UndoStack(end). UndoStack and RedoStack have the same structure:

  • Name contains the action description (presented in the figure’s menu)
  • Function is the function handle that will be invoked if the action is redone
  • Varargin are the arguments passed to Function during redo
  • InverseFunction is the function handle that will be invoked if the action is undone
  • InverseVarargin are the arguments passed to InverseFunction during undo
  • Parent and MCodeComment - I could not determine what these are used for

We can inspect the latest undo/redo actions, without activating them, by using CommandManager’s peekundo() and peekredo() methods (which return empty [] if no undo/redo action is available):

>> undoObj.CommandManager.peekredo.get % first check if isempty
             Parent: []
       MCodeComment: []
               Name: 'slider update (0.38 to 0.28)'
           Function: @internal_update
           Varargin: {[53.0037841796875]  [0.28]  [1x1 double]}
    InverseFunction: @internal_update
    InverseVarargin: {[53.0037841796875]  [0.38]  [1x1 double]}
 
>> undoObj.CommandManager.peekundo.get
             Parent: []
       MCodeComment: []
               Name: 'slider update (0.48 to 0.38)'
           Function: @internal_update
           Varargin: {[53.0037841796875]  [0.38]  [1x1 double]}
    InverseFunction: @internal_update
    InverseVarargin: {[53.0037841796875]  [0.48]  [1x1 double]}
 
>> undoObj.CommandManager.peekundo.Name
ans =
slider update (0.48 to 0.38)

We can undo/redo the latest action (last element of the UndoStack/RedoStack) by invoking CommandManager’s undo()/redo() methods. This is actually what uiundo is doing behind the scenes when it is called with the ‘execUndo’ and ‘execRedo’ arguments:

undoObj.CommandManager.undo;
undoObj.CommandManager.redo;

We can clear the entire actions stack by using CommandManager’s empty() method. This can be useful, for example, after a ‘Save’ or ‘Apply’ operation in our GUI:

undoObj.CommandManager.empty;

If we set CommandManager’s Verbose property to any non-empty value, debug information is spilled onto the Command Window when new uiundo actions are added:

>> undoObj.CommandManager.Verbose = 1;
 
% now move the slider and see the debug info below:
internal_update(h_uicontrol, [0.48,], h_uicontrol); % Called by slider update (0.28 to 0.48)
internal_update(h_uicontrol, [0.58,], h_uicontrol); % Called by slider update (0.48 to 0.58)

Finally, CommandManager uses its MaxUndoStackLength property to limit the size of the undo/redo stacks. This property is defined as read-only in %matlabroot%\toolbox\matlab\uitools\@uiundo\@CommandManager\schema.m line #12, so if you wish to programmatically modify this property from its default value of empty (=unlimited), you will need to comment out that line.

Bookmark and Share

uiundo - Matlab’s undocumented undo/redo manager

Thursday, October 29th, 2009

Whenever we have a Matlab GUI containing user-modifiable controls (edit boxes, sliders, toggle buttons etc.), we may wish to include an undo/redo feature. This would normally be a painful programming task. Luckily, there is an undocumented built-in Matlab support for this functionality via the uiundo function. Note that uiundo and its functionality is not Java-based but rather uses Matlab’s classes and the similarly-undocumented schema-based object-oriented approach.

A couple of months ago, I explained how to customize the figure toolbar. In that article, I used the undocumented uiundo function as a target for the toolbar customization and promised to explain its functionality later. I would now like to explain uiundo and its usage.

The uiundo function is basically an accessor for Matlab’s built-in undo/redo manager object. It is located in the uitools folder (%MATLABROOT%\toolbox\matlab\uitools) and its @uiundo sub-folder. To use uiundo, simply define within each uicontrol’s callback function (where we normally place our application GUI logic) the name of the undo/redo action, what should be done to undo the action, and what should be done if the user wished to redo the action after undoing it. uiundo then takes care of adding this data to the figure’s undo/redo options under Edit in the main figure menu.

For example, let’s build a simple GUI consisting of a slider that controls the value of an edit box:

hEditbox = uicontrol('style','edit', 'position',[20,60,40,40]); 
set(hEditbox, 'Enable','off', 'string','0');
hSlider = uicontrol('style','slider','userdata',hEditbox);
callbackStr = 'set(get(gcbo,''userdata''),''string'',num2str(get(gcbo,''value'')))';
set(hSlider,'Callback',callbackStr);

Simple GUI with slider update of a numeric value

Simple GUI with slider update of a numeric value

Now, let’s attach undo/redo actions to the slider’s callback. First, place the following in test_uiundo.m:

% Main callback function for slider updates
function test_uiundo(varargin)
 
  % Update the edit box with the new value
  hEditbox = get(gcbo,'userdata');
  newVal = get(gcbo,'value');
  set(hEditbox,'string',num2str(newVal));
 
  % Retrieve and update the stored previous value
  oldVal = getappdata(gcbo,'oldValue');
  if isempty(oldVal),  oldVal=0;  end
  setappdata(gcbo,'oldValue',newVal);
 
  % Prepare an undo/redo action
  cmd.Name = sprintf('slider update (%g to %g)',oldVal,newVal);
 
  % Note: the following is not enough since it only
  %       updates the slider and not the editbox...
  %cmd.Function        = @set;                  % Redo action
  %cmd.Varargin        = {gcbo,'value',newVal};
  %cmd.InverseFunction = @set;                  % Undo action
  %cmd.InverseVarargin = {gcbo,'value',oldVal};
 
  % This takes care of the update problem...
  cmd.Function        = @internal_update;       % Redo action
  cmd.Varargin        = {gcbo,newVal,hEditbox};
  cmd.InverseFunction = @internal_update;       % Undo action
  cmd.InverseVarargin = {gcbo,oldVal,hEditbox};
 
  % Register the undo/redo action with the figure
  uiundo(gcbf,'function',cmd);
end
 
% Internal update function to update slider & editbox
function internal_update(hSlider,newValue,hEditbox)
  set(hSlider,'value',newValue);
  set(hEditbox,'string',num2str(newValue));
end

And now let’s point the slider’s callback to our new function:

>> set(hSlider,'Callback',@test_uiundo);

Undo/redo functionality integrated in the figure

Undo/redo functionality integrated in the figure

We can also invoke the current Undo and Redo actions programmatically, by calling uiundo with the ‘execUndo’ and ‘execRedo’ arguments:

uiundo(hFig,'execUndo');
uiundo(hFig,'execRedo');

When invoking the current Undo and Redo actions programmatically, we can ensure that this action would be invoked only if it is a specific action that is intended:

uiundo(hFig,'execUndo','Save data');  % should equal cmd.Name

We can use this approach to attach programmatic undo/redo actions to new toolbar or GUI buttons. The code for this was given in the above-mentioned article. Here is the end-result:


Undo/redo functionality integrated in the figure toolbar

Undo/redo functionality integrated in the figure toolbar

Undo/redo functionality integrated in the figure toolbar


In my next post, due next week, I will explore advanced customizations of this functionality.

Bookmark and Share

Performance: scatter vs. line

Wednesday, October 14th, 2009

Following my previous article on the undocumented behavior of the scatter function, one of my readers, Benoit Charles, reported a discovery that in many circumstances the line function generates identical plots much faster than scatter.

Unlike scatter, line does not enable specific data-point marker customization, although the colors could be modified. On the other hand, line only uses a single handle object, saving memory and system resources compared to scatter keeping a separate handle for each data point. So, if you just need to quickly plot a bunch of scattered points then line could be a better choice than scatter.

Here is a simple code snippet, which generates identical plots and shows the performance difference:

>> x=rand(1000,1); y=rand(1000,1);
 
>> tic, for idx=1:100, cla; h=scatter(x,y); end; toc
Elapsed time is 2.521322 seconds.
 
>> props = {'LineStyle','none','Marker','o','MarkerEdge','b','MarkerSize',6};
>> tic, for idx=1:100, cla; h=line([x,x],[y,y],props{:}); end; toc
Elapsed time is 0.333369 seconds.

In the past, I have posted about other undocumented performance aspects, comparing the documented ismember function with the undocumented ismembc and about cellfun’s undocumented options. If you are aware of other similar functions having identical outputs and a significant performance difference, please let me know.

Bookmark and Share

Undocumented scatter plot behavior

Monday, October 12th, 2009

One of my blog readers, Henrik Toft, reported an undocumented behavior of the well-known scatter plot function: scatter returns a handle to an hggroup-type object that contains child handles of the data points (patch-type objects). This enables easy manipulation of specific points (for example, changing the color or marker of interesting data points).

The undocumented behavior is that when the scatter plot contains more than 100 points, the returned hggroup object only has a single child - a handle to a unified patch object that contains all the data points. This prevents customization of specific points: color can be customized via the cdata property, but this is not trivial; the marker cannot be customized at all.

Both Henrik and I initially thought this was a simple bug. But when Henrik reported this issue to MathWorks support, he got the response that this is indeed the expected behavior (i.e., not a bug but a feature), which is not documented. MathWorks did not say whether this was because they didn’t think users need more than 100 child handles, or perhaps an issue with memory/performance. Whatever the reason, I feel that this behavior/feature should at least be documented:

>> hggroup = scatter(rand(99,1),rand(99,1));
>> size(get(hggroup,'Children'))
ans =
    99     1
 
>> hggroup = scatter(rand(100,1),rand(100,1));
>> size(get(hggroup,'Children'))
ans =
   100     1
 
>> hggroup = scatter(rand(101,1),rand(101,1));
>> size(get(hggroup,'Children'))
ans =
     1     1

Workaround: use the ‘v6′ option. Unfortunately, this option is deprecated and marked as obsolete by Matlab, with the warning that it will be removed in a future Matlab version. I hope this will not be done until a better workaround for the above-reported issue is implemented in the scatter function:

>> hPatches = scatter('v6',rand(101,1),rand(101,1));
Warning: The 'v6' argument to SCATTER is deprecated, and will no longer be supported in a future release.
> In usev6plotapi>warnv6args at 84
  In usev6plotapi at 40
  In scatter at 39
 
>> size(hPatches)
ans =
   101     1

If you have detected any other undocumented behavior, function or feature in Matlab, please report them in the comments section below, or directly by email to me: altmany at gmail dot com.

Bookmark and Share

uicontrol side-effect: removing figure toolbar

Wednesday, October 7th, 2009

Earlier versions of Matlab had an undocumented side-effect in the uicontrol function, of removing the figure toolbar if the figure’s toolbar property was set to ‘auto’ (which is the default value for figures).

In order to restore the toolbar, or prevent its removal in the first place, set your figure’s toolbar property to ‘figure’. This should preferably be done immediately after creating the figure, or in its *_OpeningFcn() function if the figure was created using GUIDE:

set(hFig,'toolbar','figure');

The uicontrol issue is now documented in the latest Matlab 7.9 release (R2009b), although it was undocumented for many earlier Matlab releases. Nevertheless, the current documentation, coupled with the fact that this issue was widely reported in the CSSM newsgroup and so is not really innovative, made me hesitate before posting. But I’m currently on vacation and don’t have time for a more detailed article this week (I skipped last week entirely), so I apologize to anyone who feels disappointed. I promise to compensate when I get back from vacation…

A similar issue was reported for GUIDE-created figures, and was reportedly solved in Matlab 7.2 (R2006a).

Bookmark and Share

Undocumented mouse pointer functions

Thursday, September 24th, 2009

Matlab contains several well-documented functions and properties dealing with the mouse pointer. However, for some reason, some very-useful functions have remained undocumented and unsupported despite having a very long history (as far back as Matlab 6, a decade ago).

getptr, setptr, moveptr and overobj are all semi-documented functions (help section available, but online doc and official support not) which are used for internal Matlab purposes and relate to the mouse pointer. All these files are pure-Matlab non-Java files, which can be edited and modified. getptr and setptr (%Matlabroot\toolbox\matlab\uitools\getptr.m and setptr.m) access the pointer shape for a figure; moveptr (%Matlabroot\toolbox\shared\controllib\moveptr.m) moves the pointer across the screen; overobj (%Matlabroot\toolbox\matlab\uitools\overobj.m) determines which figure component is currently beneath the pointer.

getptr and setptr

While undocumented Java code is needed for setting component-specific pointer shapes, figure-wide shapes can be set using pure-Matlab. Setting the mouse pointer shape is normally achieved by modifying the figure’s Pointer property, which is fully documented. The following pointer shapes are supported: ‘arrow’ (the default Matlab pointer), ‘crosshair’, ‘fullcrosshair’ (used for ginput), ‘ibeam’, ‘watch’, ‘topl’, ‘topr’, ‘botl’, ‘botr’, ‘left’, ‘top’, ‘right’, ‘bottom’, ‘circle’, ‘cross’, ‘fleur’, and ‘custom’. For example:

set(hFig, 'Pointer', 'crosshair');

Using setptr enables access to a far greater variety of pointer shapes, in addition to all the standard shapes above: ‘hand’, ‘hand1′, ‘hand2′, ‘closedhand’, ‘glass’, ‘glassplus’, ‘glassminus’, ‘lrdrag’, ‘ldrag’, ‘rdrag’, ‘uddrag’, ‘udrag’, ‘ddrag’, ‘add’, ‘addzero’, ‘addpole’, ‘eraser’, ‘help’, ‘modifiedfleur’, ‘datacursor’, and ‘rotate’. It also has a few entirely-undocumented shapes: ‘forbidden’, and ‘file’ (which expects a cursor data filepath as the following argument):

setptr(gcf, 'hand');
setptr(gcf, 'file', 'my137byteFile.data');

getptr returns a cell array of parameter name/value pairs that can be stored and later used to restore the pointer shape for a specified figure:

ptr = getptr(gcf);
% do some stuff...
set(gcf, ptr{:});

moveptr

moveptr is used to move the mouse pointer programmatically across a specific plot axes. This function is poorly and incorrectly documented and contains quite a few bugs (as of R2008b). However, with minor fixes and slight attention to usage, it can be quite useful.

The regular and fully documented/supported method of moving the mouse pointer across the screen is by modifying the root (0) handle’s PointerLocation property. In many cases we may wish to programmatically move the pointer from to a specific location in a plot axes. This can be done of course by computing the figure’s dimensions and position in screen pixel coordinates, and the internal figure components dimensions/position within the figure, and their internal components and so on, not forgetting to translate data or normalized coordinates into screen pixel units, and taking care of logarithmic and reverse axes. After adding all these up, and assuming we’ve made no mistake in the computation (no easy task), we can now set the root handle’s PointerLocation property.

Comes moveptr to the rescue: moveptr accepts a handle(hAxes) as first parameter (note that handle(hAxes) must be passed, not hAxes!), and one of the following; either ‘init’ to initialize the move (creating a transformation from local axes data units to screen pixels), or ‘move’ followed by X,Y (axes data units) to actually move the mouse pointer. X and Y may well be outside the axes boundaries:

moveptr(handle(gca),'init');
moveptr(handle(gca),'move',-5,7);  % so easy

moveptr is only available in Matlab 7, not Matlab 6. moveptr also has a bug/limitation (fixed in R2008a) in that it expects the supplied axes to be a direct child of the figure. Finally, it accepts handle(hAxes) as first parameter – not the regular numeric hAxes handle as its help section would have us believe. These may not of course be the case in a more general case. Note that R2008a’s fix for the direct-figure-child limitation, using hgconvertunits (another semi-documented function), is incompatible with Matlab 6, which did not have this function. The fix for all these issues, which accepts both hAxes and handle(hAxes), removes the direct-figure-child limitation and also works on Matlab 6, is outlined below:

%% Moveptr replacement for Matlab 6 compatibility
function moveptr(hAx, x, y)
    % Compute normalized axis coordinates
    NormX = getNormCoord(hAx, 'x', x);
    NormY = getNormCoord(hAx, 'y', y);
 
    % Compute the new coordinates in screen units
    Transform = axis2Screen(hAx);
    NewLoc = Transform(1:2) + Transform(3:4) .* [NormX NormY];
 
    % Move the pointer
    set(0,'PointerLocation',NewLoc);
%end  % moveptr
 
%% Get normalized axis coordinates
function normCoord = getNormCoord(hAx, axName, curPos)
    limits = get(hAx, [axName 'Lim']);
    if strcmpi(get(hAx,[axName 'Scale']), 'log')
        normCoord = (log2(curPos) - log2(limits(1))) /diff(log2(limits));
    else
        normCoord = (curPos-limits(1)) / diff(limits);
    end
%end  % getNormCoord
 
%% Axis to screen coordinate transformation
function T = axis2Screen(ax)
    % computes a coordinate transformation T = [xo,yo,rx,ry] that
    % relates normalized axes coordinates [xa,ya] of point [xo,yo]
    % to its screen coordinate [xs,ys] (in the root units) by:
    %     xs = xo + rx * xa
    %     ys = yo + ry * ya
    % Note: this is a modified internal function within moveptr()
    % Get axes normalized position in figure
    T = getPos(ax,'normalized');
    % Loop all the way up the hierarchy to the root
    % Note: this fixes a bug in Matlab 7's moveptr implementation
    parent = get(ax,'Parent');
    while ~isempty(parent)
        % Transform normalized axis coords -> parent coords
        if isequal(parent,0)
            parentPos = get(0,'ScreenSize');  % Save screen units
        else
            parentPos = getPos(parent, 'normalized'); % Norm units
        end
        T(1:2) = parentPos(1:2) + parentPos(3:4) .* T(1:2);
        T(3:4) = parentPos(3:4) .* T(3:4);
        parent = get(parent,'Parent');
    end
%end  % axis2Screen

overobj and hittest

overobj is a related semi-documented function. It returns a handle to the first visible element beneath the mouse pointer that has visible handles (i.e., can be found with findobj , as opposed to hidden handles that can only be found using findall). The mandatory Type argument specifies the requested handle’s Type property value.

There are several overobj usage samples in CSSM. Here is an example for modifying the mouse cursor over axes:

% Modify mouse pointer over axes
hAxes = overobj('axes');
if ~isempty(hAxes)
    set(gcf,'Pointer','fleur');
else
    set(gcf,'Pointer','arrow');
end

A CSSM reader has noted that since overobj uses the findobj function, it is rather slow. Therefore, do not use overobj in a complex figure’s WindowButtonMotionFcn callback.

overobj has other annoying limitations: it only searches direct figure children, not inner descendants. Therefore, anything contained within a uipanel, uibuttongroup etc. cannot be found. Also, it assumes the root and figure units are both ‘pixel’: often they are not. Also, it only searches visible objects: hidden axes are not retrieved. Finally, it would be a great benefit to have an overobj variant in which the Type argument is optional, so the function would return the handle of the first object found, of whichever type. Here’s this variant overobj2, adapted from Matlab’s overobj. Note that overobj and overobj2 only work on objects having a Position property, and so cannot be used for axes plot children:

function h = overobj2(varargin)
%OVEROBJ2 Get handle of object that the pointer is over.
%   H = OVEROBJ2 searches all objects in the PointerWindow
%   looking for one that is under the pointer. Returns first
%   object handle it finds under the pointer, or empty matrix.
%
%   H = OVEROBJ2(FINDOBJ_PROPS) searches all objects which are
%   descendants of the figure beneath the pointer and that are
%   returned by FINDOBJ with the specified arguments.
%
%   Example:
%       h = overobj2('type','axes');
%       h = overobj2('flat','visible','on');
%
%   See also OVEROBJ, FINDOBJ
 
% Ensure root units are pixels
oldUnits = get(0,'units');
set(0,'units','pixels');
 
% Get the figure beneath the mouse pointer and mouse pointer pos
fig = get(0,'PointerWindow'); 
p = get(0,'PointerLocation');
set(0,'units',oldUnits);
 
% Look for quick exit (if mouse pointer is not over any figure)
if fig==0,  h=[]; return;  end
 
% Compute figure offset of mouse pointer in pixels
figPos = getpixelposition(fig);
x = (p(1)-figPos(1));
y = (p(2)-figPos(2));
 
% Loop over all figure descendents
c = findobj(get(fig,'Children'),varargin{:});
for h = c',
   % If descendent contains the mouse pointer position, exit
   r = getpixelposition(h);
   if ( (x>r(1)) && (x<r(1)+r(3)) && (y>r(2)) && (y<r(2)+r(4)) )
      return
   end
end
h = [];

An alternative to overobj or overobj2 is to use the undocumented hittest built-in function – separate cases may dictate preferring one alternative to the other:

hObj = hittest(hFig);

p.s. - while most of Matlab’s undocumented stuff indeed lies in the Java domain, there are quite a few non-Java pearls to be exposed, as in this article. From time to time I will take time off from Java and describe these aspects.

Bookmark and Share