Archive for the ‘Low risk of breaking in future versions’ Category

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 HTML panel

Tuesday, December 15th, 2009

Last week, I explained how a browser control can be integrated in Matlab GUI applications. Sometimes we only need to display simple HTML, for which a full browser seems like overkill. Moreover, we may wish to edit the displayed contents, which cannot be done using the browser control. The solution is to use a standard Java Swing JEditorPane control, which is an editable HTML-aware control.

Oddly enough, it was only yesterday that Mikhail, a known Matlab Java specialist on the CSSM newsgroup, posted an example for this as answer to a question on the StackOverflow forum (slightly edited for clarity):

mytext = ['<html><body><table border="1">' ...
          '<tr><th>Month</th><th>Savings</th></tr>' ...
          '<tr><td>January</td><td>$100</td></tr>' ...
          '</table></body></html>'];
 
% Create a figure with a scrollable JEditorPane
hfig = figure();
je = javax.swing.JEditorPane('text/html', mytext);
jp = javax.swing.JScrollPane(je);
[hcomponent, hcontainer] = javacomponent(jp, [], hfig);
set(hcontainer, 'units', 'normalized', 'position', [0,0,1,1]);
 
% Turn anti-aliasing on (R2006a, Java 5.0)
java.lang.System.setProperty('awt.useSystemAAFontSettings', 'on');
je.setFont(java.awt.Font('Arial', java.awt.Font.PLAIN, 13));
je.putClientProperty(javax.swing.JEditorPane.HONOR_DISPLAY_PROPERTIES, true);
 
% This only works on Java 1.5 (Matlab R14SP2 to R2007a):
je.putClientProperty(com.sun.java.swing.SwingUtilities2.AA_TEXT_PROPERTY_KEY, true);

Editable HTML-aware JEditorPane

Editable HTML-aware JEditorPane

Mikhail’s code included setting SwingUtilities2’s AA_TEXT_PROPERTY_KEY property for anti-aliasing. Unfortunately, SwingUtilities2 was an unsupported and undocumented internal class in Java 1.5 (undocumented/unsupported by Sun, not MathWorks for a change…) and completely disappeared in Java 1.6 (which is bundled with Matlab R2007b onward). Therefore, SwingUtilities2 can only be used on Matlab releases R14SP2 (7.0.4) through R2007a (7.4) - on any other Matlab version this will throw an error.

Alternately, use JIDE’s AA_TEXT_PROPERTY_KEY (JIDE is bundled with Matlab and this is supported even on new Matlab releases - I will present JIDE in future articles).

property = com.jidesoft.swing.JideSwingUtilities.AA_TEXT_PROPERTY_KEY;
je.putClientProperty(property, true);

Or, simply add the following switch to your java.opt file:

-Dswing.aatext=true

With this switch, you no longer need to set anti-aliasing separately for each component. It is entirely harmless to set this switch even on Matlab/Java versions that do not support it (the switch is simply ignored in these cases).

Note that while JEditorPane’s support for HTML is extensive, it is incomplete. It also does not contain a JavaScript engine or other web-related features we have come to expect in a browser. For the more complex stuff we can use the browser control as explained in last week’s article.

Matlab’s own multi-line editbox uicontrol uses JEditorPane (or actually its derived-class JTextPane) as an underlying component. This means that the simple-looking Matlab editbox is actually a powerful HTML-aware component. In order to use these hidden undocumented features we need the editbox’s underlying JTextPane handle. This is done using the FindJObj utility, which will be described in my next article. Following that, I will show how to customize Matlab’s dull-looking editbox into something much more powerful. Here’s a sample, to help you stay tuned:

HTML contents in a regular Matlab editbox

HTML contents in a regular Matlab editbox

Bookmark and Share

Undocumented XML functionality

Wednesday, November 18th, 2009

Matlab’s built-in XML-processing functions have several undocumented features that can be used by Java-savvy users. We should note that the entire XML-support functionality in Matlab is java-based. I understand that some Matlab users have a general aversion to Java, some even going as far as to disable it using the -nojvm startup option. But if you disable Java, Matlab’s XML functions will simply not work. Matlab’s own documentation points users to Sun’s official Java website for explanations of how to use the XML functionality (the link in the Matlab docpage is dead - the correct link should probably be https://jaxp-sources.dev.java.net/nonav/docs/api/, but Sun keeps changing its website so this link could also be dead soon…).

Using the full Java XML parsing (JAXP) functionality is admittedly quite intimidating for the uninitiated, but extremely powerful once you understand how all the pieces fit together. Over the years, several interesting utilities were submitted to the Matlab File Exchange that simplify this intimidating post-processing. See for example XML parsing tools, the extremely popular XML Toolbox and xml_io_tools, the recent XML data import and perhaps a dozen other utilities.

Each of Matlab’s main built-in XML-processing functions, xmlread, xmlwrite and xslt has an internal set of undocumented and unsupported functionalities, which builds on their internal Java implementation. As far as I could tell, these unsupported functionalities were supported at least as early as Matlab 7.2 (R2006a), and possibly even on earlier releases. For the benefit of the Java and/or JAXP -speakers out there (it will probably not help any others), I list Matlab’s internal description of these unsupported functionalities, annotated with API hyperlinks. These description (sans the links) can be seen by simply editing the m file, as in (the R2008a variant is described below):

edit xmlread

xmlread

function [parseResult,p] = xmlread(fileName,varargin)
  • FILENAME can also be an InputSource, File, or InputStream object
  • DOMNODE = XMLREAD(FILENAME,…,P,…) where P is a DocumentBuilder object
  • DOMNODE = XMLREAD(FILENAME,…,’-validating’,…) will create a validating parser if one was not provided.
  • DOMNODE = XMLREAD(FILENAME,…,ER,…) where ER is an EntityResolver will set the EntityResolver before parsing
  • DOMNODE = XMLREAD(FILENAME,…,EH,…) where EH is an ErrorHandler will set the ErrorHandler before parsing
  • [DOMNODE,P] = XMLREAD(FILENAME,…) will return a parser suitable for passing back to XMLREAD for future parses.

xmlwrite

function xmlwrite(FILENAME,DOMNODE);
function str = xmlwrite(DOMNODE);
function str = xmlwrite(SOURCE);

xslt

function [xResultURI,xProcessor] = xslt(SOURCE,STYLE,DEST,varargin)
  • SOURCE can also be a XSLTInputSource
  • STYLE can also be a StylesheetRoot or XSLTInputSource
  • DEST can also be an XSLTResultTarget. Note that RESULT may be empty in this case since it may not be possible to determine a URL. If STYLE is absent or empty, the function uses the stylesheet named in the xml-stylesheet processing instruction in the SOURCE XML file. (This does not always work)
  • There is also an entirely undocumented feature: passing a ‘-tostring’ input argument transforms the inputs into a displayed text segment, rather than into a displayed URI; the transformed text is returned in the xResultURI output argument.

Note: internal comments within the Matlab code seem to indicate that XSLT is SAXON-based, so interested users might use SAXON’s documentation for accessing additional XSLT-related features/capabilities.

Bookmark and Share

Customizing Matlab labels

Wednesday, November 11th, 2009

As I was deliberating the topic of my weekly article, a new CSSM newsreader thread arrived today to immediately conclude the debate: The CSSM poster asked how Matlab labels can be modified to display non-ASCII characters such as the ∀ or β math symbols.

As you may recall, unlike axes text labels that support Tex/Latex, and unlike other uicontrols like buttons or listboxes that support HTML, text labels (uicontrol(’Style’,'text’,…)) do not support text formatting or special symbols.

In the above-mentioned thread, Matt Whitaker, a longstanding CSSM contributor and a Matlab-Java veteran gave a solution that shows how seemingly difficult questions sometimes have simple solutions right beneath our noses. His solution was to simply replace the uicontrol label with a Java JLabel:

%show the 'for all' and 'beta' symbols
labelStr = '<html>&#8704;&#946; <b>bold</b> <i><font color="red">label</html>';
jLabel = javaObjectEDT('javax.swing.JLabel',labelStr);
[hcomponent,hcontainer] = javacomponent(jLabel,[100,100,40,20],gcf);

Math symbols and HTML support in a Java JLabel

Math symbols and HTML support in a Java JLabel

Note that the standard Matlab text uicontrol itself is very limited in the amount of customization it supports, even when accessing its underlying Java object using the FindJObj utility. This underlying Java object is a com.mathworks.hg.peer.utils.MultilineLabel extension of Swing’s bland javax.swing.JComponent. In fact, aside from some font and color customizations (also available via the Matlab HG properties), the most useful properties that are accessible only via the Java object are few. These include Border, HorizontalAlignment, VerticalAlignment and LineWrap. This is a very short list compared to the long list of corresponding undocumented properties in the other uicontrols.

Bookmark and Share

Updating speaker sound volume

Thursday, October 15th, 2009

In a recent CSSM forum post, a reader asked whether it is possible to get and/or set the system speaker sound volume programmatically. The purpose would be to connect this programming snippet to a GUI control that would allow users to increase/decrease playback volume in the same GUI window as the other playback controls.

This is apparently not possible with documented Matlab functions: we can modify specific playback files, but not the speaker in general. However, it is possible using standard Java calls. Note that I am specifically not mentioning the word undocumented here. The reason is that these Java calls can all be achieved using Matlab’s documented, although mostly unknown, Java interface.

Today, I have submitted a new utility to the Matlab File Exchange, called SoundVolume. SoundVolume enables getting and setting the speaker volume programmatically. For the benefit of users, here is the core of the program (the actual program contains extra sanity checks and exception handling):

function volume = SoundVolume(volume)
 
   % Loop over the system's mixers to find the speaker port
   import javax.sound.sampled.*
   mixerInfos = AudioSystem.getMixerInfo;
   foundFlag = 0;
   for mixerIdx = 1 : length(mixerInfos)
      mixer = AudioSystem.getMixer(mixerInfos(mixerIdx));
      ports = getTargetLineInfo(mixer);
      for portIdx = 1 : length(ports)
         port = ports(portIdx);
         try
            portName = port.getName;  % better
         catch   %#ok
            portName = port.toString; % sub-optimal
         end
         if ~isempty(strfind(lower(char(portName)),'speaker'))
            foundFlag = 1;  break;
         end
      end
   end
   if ~foundFlag
      error('Speaker port not found');
   end
 
   % Get and open the speaker port's Line object
   line = AudioSystem.getLine(port);
   line.open();
 
   % Loop over the Line's controls to find the Volume control
   ctrls = line.getControls;
   foundFlag = 0;
   for ctrlIdx = 1 : length(ctrls)
      ctrl = ctrls(ctrlIdx);
      ctrlName = char(ctrls(ctrlIdx).getType);
      if ~isempty(strfind(lower(ctrlName),'volume'))
         foundFlag = 1;  break;
      end
   end
   if ~foundFlag
      error('Volume control not found');
   end
 
   % Get or set the volume value according to the user request
   oldValue = ctrl.getValue;
   if nargin
      ctrl.setValue(volume);
   end
   if nargout
      volume = oldValue;
   end

Note that our Matlab code could have been much simpler, following the examples provided here and here. Unfortunately, Matlab prevents using Java classnames containing a period (e.g., Port.Info) or static Interface values, so we cannot directly access Port.Info.SPEAKER or FloatControl.Type.VOLUME. I’ve reported this bug to Mathworks earlier this year, and I do not know in which Matlab release (if at all) they intend to fix it. Until then we need to use workarounds, as in the code above. I posted a similar issue (and its workaround) earlier in this blog, when setting system-tray popup message.

For more information about accessing Java’s extensive sound-related functionality, read Sun’s official Java sound documentation trail.

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

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

Spicing up Matlab uicontrol tooltips

Wednesday, May 27th, 2009

Outside the official Matlab blogs, perhaps the most widely known Matlab-related active blog is BlinkDagger. This blog is certainly worth following, especially for novice Matlab users who could gain fresh angles on regular programming tasks that have a simple solution in Matlab.

In one of their latest posts, BlinkDagger described how to use tooltips in Matlab GUI. In one of this blog’s very first posts, I described how HTML can easily be used with Matlab uicontrols. Let’s now combine these ideas to show how HTML support can easily be used to spice-up the tooltips.

Let’s start with a simple styled multi-line tooltip:

str = '<html><b>line #1</b><br><i><font color="red">line#2';
set(hControl,'tooltipString',str);

Multi-line HTML'ed tooltip

Multi-line HTML'ed tooltip

This technique was used to display the informative tooltip for my Java-based data table utility on the Matlab file Exchange:

Multi-line HTML-styled tooltip

Multi-line HTML-styled tooltip

Tooltips can also be used to present images, using the HTML <img> tag. However, the image src (filename) needs to be formatted in a URL-compliant format such as ‘http://undocumentedmatlab.com/images/table.png‘ or ‘file:/C:\Yair\Undocumented Matlab\Images\table.png’.

If you try to use a non-URL-format filename, the image will not be displayed. Instead, a placeholder box will appear. For example, let’s take the table screenshot above and try to place its filename directly in the tooltip HTML:

filePath = 'C:\Yair\Undocumented Matlab\Images\table.png';
str = ['<html><center><img src="' filePath '"><br>' filePath];
set(hButton,'tooltipString',str);

Tooltip with invalid HTML img source URL

Tooltip with invalid HTML img source URL

If we fix filePath to be a valid URL, it now looks as intended:

filePath = 'C:\Yair\Undocumented Matlab\Images\table.png';
filePath = strrep(['file:/' filePath],'\','/');
str = ['<html><center><img src="' filePath '"><br>' ...
       '<b><font color="blue">' filePath];
set(hButton,'tooltipString',str);

Tooltip with HTML image and caption


Tooltip with HTML image and caption

Note that the tooltip looks enormous (it’s actually even downsized to fit this post…). This is because our HTML <img> was not limited in size and so the tooltip was created to display the screenshot in its original large size. In order to limit the tooltip size, simply add the height and width attributes to the HTML <img> tag, remembering to preserve the original image aspect ratio.

Now that we know the basics, we can really go wild with HTML and CSS formatting. Have you configured any kick-butt tooltip in your application? If so, please share it here.

Bookmark and Share