Archive for the ‘Figure window’ Category

FindJObj GUI - display container hierarchy

Tuesday, January 12th, 2010

In my previous post, I explained how the findjobj utility can be used to access a Matlab component’s underlying Java component. Findjobj has another role: displaying the component hierarchy of complex Matlab containers such as the figure window, GUIDE or the Editor.

When findjobj is called with no output arguments, the function infers that the user requests to see the GUI version, rather than to get the control’s Java handle:

>> findjobj(gcf);  % or: findjobj(gcf)

FindJObj GUI (click to zoom)

FindJObj GUI (click to zoom)

There are several note-worthy aspects in this graphical hierarchy presentation:

The hierarchy tree itself is displayed using the internal com.mathworks.hg.peer.UITreePeer Java object. This is the object that underlies the semi-documented uitree function. The hierarchy sub-components are presented as tree nodes, each having a separate icon based on the component type. In some cases (toolbar buttons for example), the component’s icon image is used for its corresponding tree node. A javax.swing.JProgressBar is presented while the tree is being populated, an action that can take a few seconds depending on the target figure’s complexity. Some tree branches which are normally uninteresting are automatically collapsed: hidden containers (these are also grayed-out), menubars, toolbars and scrollbars. In parallel to the Java container hierarchy, a separate tree branch is presented with the corresponding Matlab (Handle-Graphics, or HG) hierarchy.

Another GUI example - note the hidden (gray) items, the HG tree branch and the auto-collapsed MJToolBar container

Another GUI example - note the hidden (gray) items, the HG tree branch and the auto-collapsed MJToolBar container

Each node item gets a unique tooltip (see top screenshot above). Similarly, a unique context-menu (right-click menu) is attached to each node item with actions that are relevant for that node:

Item-specific context-menu

Item-specific context-menu

Finally, a node-selection callback is attached to the tree, that will flash a red border around the GUI control when its corresponding Java node-item is clicked/selected:

FindJObj - flashing red border around a toolbar icon

FindJObj - flashing red border around a toolbar icon

Once the tree was done, I set out to display and enable modifications of component properties and callbacks in separate adjacent panels. I used the internal com.mathworks.mlwidgets.inspector.PropertyView component to display the properties (this is the JIDE component that underlies the built-in inspect function). To prevent a JIDE run-time alert, I called com.mathworks.mwswing.MJUtilities.initJIDE. A label is added to the table’s header, displaying the currently selected sub-component’s class (e.g., “javax.swing.JButton”), and a tooltip with a color-coded list of all the control’s properties.

The callbacks table was implemented using com.jidesoft.grid.TreeTable to enable easy column resizing, but this is otherwise used as a simple data table. A checkbox was added to filter out the 30-odd standard Swing callbacks, which are non-unique to the selected sub-component (tree node). All the panels - tree, properties and callbacks - are then placed in resizable javax.swing.JSplitPanes and presented to the user.

I have omitted mention of some other undocumented features in findjobj. After all, space here is limited and the function is over 2500 lines long. I encourage you to download the utility and explore the code, and I gladly welcome your feedback.

Bookmark and Share

FindJObj - find a Matlab component’s underlying Java object

Wednesday, January 6th, 2010

In a previous post, I explained that all Matlab GUI (except the axes plotting engine) is based on Java components, and showed how we can use this information to display HTML contents in Matlab uicontrols. In other posts, I have shown how a utility called findjobj can be used to access the underlying Java components to enable customizations that are unavailable in standard Matlab: setting the line location in an edit-box, customizing button appearance, setting uicontrol callbacks, or setting list-box mouse actions. I have also shown how findjobj can be used to display the component hierarchy of complex Matlab containers such as the figure window, GUIDE or the Editor.

The time is therefore well overdue for a formal introduction of findjobj, explaining its uses and internal mechanism. Of course, readers are welcome to continue using findjobj as a black-box utility, but I think important insight can be gained from understanding its inner details. Findjobj’s code is available for free download on the MathWorks File Exchange. It is one of my favorite submissions and is apparently well-liked by users, being highly reviewed and highly downloaded.

Findjobj has two main purposes:

  1. Find the underlying Java object reference of a given Matlab handle - Historically this was the original purpose, hence the utility’s name. Findjobj was meant to extend Matlab’s standard findobj function, which does not expose Java components.
  2. Display a container’s internal components hierarchy in a graphical user interface, to facilitate visualization of complex containers. This was later extended to also display and allow modification of the sub-components’ properties and callbacks.

Today I will focus on the first (programmatic) aspect; next week I will describe the second (GUI) aspect.

Findjobj’s heart is finding a control’s underlying Java handle. Unfortunately, this is not exposed by Matlab except in very rare cases. As hard as I tried, I could not find a way to directly access the underlying Java-peer handle. I therefore resorted to getting the control’s enclosing Java frame (window) reference, and then working down its sub-components hierarchy until finding the Java object(s) which satisfy the position and/or class criteria. To get the enclosing Java frame (aka TopLevelAncestor), I use the Matlab figure’s undocumented JavaFrame property. Using this property issues a standard warning (since Matlab release R2008a) of becoming obsolete in some future Matlab release. Since it worked so far, I have turned off this warning in findjobj’s code, but note that this code may well fail in some future Matlab version. If and when JavaFrame does become obsolete, be sure to look in this blog for workarounds…

Traversing the frame’s hierarchy presents several challenges: Main-menu items are accessed using different functions than other Swing components or sub-containers, and are not automatically accessible until first displayed. I have overcome this latter challenge by simulating a menu-open action in case menus should be searched (this is off by default since it takes several seconds and also changes the GUI focus). For “regular” sub-containers, sometimes we need to loop over getComponent(…) and in some other cases over getChildAt(…).

Another challenge was presented by the fact that Java positions start at (0,0) in the top left corner increasing rightward and downward, rather than starting at (1,1) in the bottom left and increasing upward as in Matlab. Moreover, Java positions are always pixel-based and relative to their parent container, which is different from Matlab (if the Matlab units is ‘pixels’ then the value is absolute; if ‘normalized’ then it returns a non-pixel value). To further complicate matters, some Matlab controls have a different size than their Java counterparts: some controls have a 5-pixel margins while others not, some controls are shifted by a pixel or two from their container’s border (for a total offset of up to 7 pixels), while some controls (such as popup-menus) have an entirely different reported size. In theory, we could use the Matlab component’s undocumented PixelBounds property (much faster than getpixelposition), but unfortunately PixelBounds turns out to be unreliable and returns erroneous values in many cases. Finally, different Java containers/components have different ways of returning their position: for some it is a getLocation() method, for others it is getX()/getY() and for others it is the X and Y properties (that sometimes have no corresponding getX()/getY() accessor methods!).

Having finally overcome all these challenges (and quite a few smaller ones, documented within the source code), I have wrapped the algorithm in a function interface that tries to emulate findobj’s. Using findjobj can now be as easy as:

% Modify the mouse cursor when over the button
hButton = uicontrol('string','click me!');
jButton = findjobj(hButton);
jButton.setCursor(java.awt.Cursor(java.awt.Cursor.HAND_CURSOR))

Modified uicontrol cursor - a Java property

Modified uicontrol cursor - a Java property

…or as complex as:

% Find all non-button controls with the specified label
jControls = findjobj('property',{'text','click me!'}, 'not','class','button');

Space here is limited and findjobj is over 2500 lines long, so I have obviously not covered everything. I encourage you to download the utility and explore the code, and I gladly welcome your feedback.

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

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

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

Detecting window focus events

Wednesday, September 9th, 2009

A CSSM reader recently asked whether it is possible to detect window focus events (specifically, the focus-gain event) asynchronously, so that such events can trigger a callback without necessitating a polling thread to constantly monitor the windows state.

The user correctly mentioned the fact that although mouse-clicks within the window frame can be detected using the documented figure callback WindowButtonDownFcn, there are other methods by which a window can gain focus: keyboard (<Alt>-<Tab> on Windows, for example), clicking the window frame edge etc. These methods are all undetected by WindowButtonDownFcn.

This problem is, to the best of my knowledge, insoluble using standard documented Matlab. However, there is indeed a simple solution using undocumented/unsupported Matlab features. The solution relies on the fact that all Matlab windows are basically Java Swing objects, and these objects have dozens of standard callback hooks that can be utilized (Matlab only exposes a few callbacks). The list of standard Swing callbacks was detailed in my earlier article about uicontrol callbacks, which is also relevant for Java window frames.

In this specific case, we are interested in FocusGainedCallback. This callback is invoked for the figure Frame’s AxisComponent (a part of the Frame that will be explained in another article). For each of our monitored figure windows, we set this callback to a predefined Matlab function. We may also wish to set its companion FocusLostCallback.

Here’s the resulting code snippet (hFig is our Matlab figure handle):

% Prepare the figure
hFig = figure;  % etc. - prepare the figure
 
% Get the underlying Java reference
warning off MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame
jFig = get(hFig, 'JavaFrame');
jAxis = jFig.getAxisComponent;
 
% Set the focus event callback
set(jAxis,'FocusGainedCallback',{@myMatlabFunc,hFig});
% perhaps also set the FocusLostCallback here

Whenever any of the monitored figures now gets focus, by whichever means, the user-defined Matlab function myMatlabFunc() will be invoked. This function should be defined as follows:

function myMatlabFunc(jAxis, jEventData, hFig)
   % do whatever you wish with the event/hFig information
end

Extra input parameters can be added during callback setup and definition, as follows:

set(jAxis,'FocusLostCallback',{@myMatlabFunc,hFig,data1,data2})
...
function myMatlabFunc(jAxis, jEventData, hFig, data1, data2)
   % do whatever you wish with the event/hFig/data information
end

A very similar technique can detect other windowing events (maximization/minimization/movement etc.). Depending on the case, you may need to use jFig.fFigureClient.getWindow instead of jFig.getAxisComponent. The list of available callbacks for each of these objects can be seen using a simple set(jFig.getAxisComponent) command, or via my UIInspect or FindJObj utilities on the Matlab File Exchange.

Note that all this relies on the undocumented hidden figure property JavaFrame, which issues a standing warning (since Matlab release R2008a) of becoming obsolete in some future Matlab release. Since it worked so far, I have turned off this warning in the code above, but note that this code may well fail in some future Matlab version. If and when JavaFrame does become obsolete, be sure to look in this blog for workarounds…

Bookmark and Share

Figure toolbar customizations

Wednesday, September 2nd, 2009

Last week, I described how to access existing Matlab figure toolbar icons and how to add non-button toolbar components. Today, I describe how the toolbar itself can be customized using undocumented functionality and properties.

All the important undocumented customizations can only be accessed via the toolbar’s Java handle, which is retrieved so:

hToolbar = findall(hFig,'tag','FigureToolBar');
jToolbar = get(get(hToolbar,'JavaContainer'),'ComponentPeer');

One interesting functionality is enabling a floating toolbar, via jToolbar.setFloatable(1). The toolbar can then be dragged from its docked position at the top of the figure menu, becoming enclosed in an independent floating window (a non-modal javax.swing.JDialog child of the parent figure, to be exact). Since this toolbar window has a very small initial size and no name, a simple immediate fix is required:

% Modify Java toolbar properties
jToolbar.setFloatable(1);
hjToolbar = handle(jToolbar,'CallbackProperties');
set(hjToolbar,'AncestorAddedCallback',@dockUndockCallbackFcn);
 
% Sample dockUndockCallbackFcn function
function dockUndockCallbackFcn(hjToolbar, eventdata)
   if hjToolbar.isFloating
      jToolbarWin = hjToolbar.getTopLevelAncestor;
      jToolbarWin.setTitle('Toolbar');
      %jToolbarWin.setResizable(1); %if you wish manual resize
      jToolbarWin.setPreferredSize(java.awt.Dimension(380,57));
      jToolbarWin.setSize(java.awt.Dimension(380,57));
      jToolbar.revalidate;  %repaint toolbar
      jToolbarWin.getParent.validate; %repaint parent figure
   end
end

Floating toolbar   ...and after minor fixes

Floating toolbar                 ...and after minor fixes        

Re-docking a floating toolbar can be done by simply closing the floating window – the toolbar then reappears in its default (top) position within the parent figure window.

There are other interesting functions/properties available via the Java interface – readers are encouraged to explore via the methods, methodsview, inspect functions, or my uiinspect utility.

For example, addGap() can be used to add a transparent gap between the rightmost toolbar component and the window border: this gap is kept even if the window is shrunk to a smaller width – the rightmost components disappear, maintaining the requested gap.

setBackground() sets the background color that is seen beneath transparent pixels of button images and gaps. Non-transparent (opaque or colored) pixels are not modified. If the button icons are improperly created, the result looks bad:

jToolbar.setBackground(java.awt.Color.cyan); %or: Color(0,1,1)

Default figure toolbar with cyan background

Default figure toolbar with cyan background

This problem can be fixed by looping over the toolbar icons and modifying the pixel values from their default gray background to transparent. An example for this practice was given at the beginning of last week’s article.

setMorePopupEnabled() is used to specify the behavior when the window resizes to such a small width that one or more toolbar buttons need to disappear – by default (=1 or true) the chevron (>>) mark appears on the toolbar’s right, enabling display of the missing buttons, but this behavior can be overridden (0 or false) to simply crop the extra buttons.

setRollover() controls the behavior when the mouse passes (”rolls”) over toolbar buttons. The default parameter (1 or true), displays a 3-dimensional button border, creating an embossing effect; this can be overridden (0 or false) to use a different 3D effect:

% Set non-default Rollover, MorePopupEnabled
jToolbar.setRollover(0);         % or: set(jToolbar,'Rollover','off');
jToolbar.setMorePopupEnabled(0); % or: set(jToolbar,'MorePopupEnabled','off');

default Rollover & MorePopupEnabled properties

non-default Rollover & MorePopupEnabled properties

default (top) and non-default (bottom)
Rollover & MorePopupEnabled properties

Remember that toolbars are simply containers for internal components, generally buttons and separators. These components may be accessed individually and manipulated. An example of such manipulation can be found in my FindJObj utility on the File Exchange, that lists the individual figure components: whenever the user selects a toolbar button (or any other Java component for that matter), its border is temporarily modified to a flashing red rectangle helping users understand the component’s location. Here’s the relevant code snip and screenshot (readers are encouraged to look at the actual code, which is more complex - FindJObj sub-function flashComponent()):

% Prepare the red border panel
oldBorder = jComponent.getBorder;
redBorder = javax.swing.border.LineBorder(java.awt.Color.red,2,0);
redBorderPanel = javax.swing.JPanel;
redBorderPanel.setBorder(redBorder);
redBorderPanel.setOpaque(0);  % transparent interior, red border
redBorderPanel.setBounds(jComponent.getBounds);
isSettable(compIdx) = ismethod(jComponent,'setBorder');
 
% flash by periodically displaying/hiding the panel
for idx = 1 : 2*numTimes
   if idx>1,  pause(delaySecs);  end  % don't pause at start
   visible = mod(idx,2);
   jParent = jComponent.getParent;
 
   % Most Java components allow modifying their borders
   if isSettable
      if visible
         % Set a red border
         jComp.setBorder(redBorder);
         try jComponent.setBorderPainted(1); catch, end
      else %if ~isempty(oldorder)
         % Remove red border by restoring the original border
         jComp.setBorder(oldBorder);
      end
      jComp.repaint;
 
   % Other Java components are highlighted by a transparent red-
   % border panel, placed on top of them in their parent's space
   elseif ~isempty(jParent)
      if visible
         % place the transparent red-border panel on top
         jParent.add(redBorderPanel);
         jParent.setComponentZOrder(redBorderPanel,0);
      else
         jParent.remove(redBorderPanel);
      end
      jParent.repaint;
   end
end  % idx flash loop

FindJObj - flashing red border around a toolbar icon

FindJObj - flashing red border around a toolbar icon

Bookmark and Share

Figure toolbar components

Thursday, August 27th, 2009

Toolbars are by now a staple of modern GUI design. An unobtrusive list of small icons enables easy access to multiple application actions without requiring large space for textual descriptions. Unfortunately, the built-in documented support for the Matlab toolbars is limited to adding icon buttons via the uipushtool and uitoggletool functions, and new toolbars containing them via the uitoolbar function. In this post I will introduce several additional customizations that rely on undocumented features.

This article will only describe figure toolbars. However, much of the discussion is also relevant to the desktop (Comand Window) toolbars and interested users can adapt it accordingly.

Accessing toolbar buttons - undo/redo

Let’s start by adding undo/redo buttons to the existing figure toolbar. I am unclear why such an elementary feature was not included in the default figure toolbar, but this is a fact that can easily be remedied. In a future post I will describe uiundo, Matlab’s semi-documented support for undo/redo functionality, but for the present let’s assume we already have this functionality set up.

First, let’s prepare our icons, which are basically a green-filled triangle icon and its mirror image:

% Load the Redo icon
icon = fullfile(matlabroot,'/toolbox/matlab/icons/greenarrowicon.gif');
[cdata,map] = imread(icon);
 
% Convert white pixels into a transparent background
map(find(map(:,1)+map(:,2)+map(:,3)==3)) = NaN;
 
% Convert into 3D RGB-space
cdataRedo = ind2rgb(cdata,map);
cdataUndo = cdataRedo(:,[16:-1:1],:);

Now let’s add these icons to the default figure toolbar:

% Add the icon (and its mirror image = undo) to the latest toolbar
hUndo = uipushtool('cdata',cdataUndo, 'tooltip','undo', 'ClickedCallback','uiundo(gcbf,''execUndo'')');
hRedo = uipushtool('cdata',cdataRedo, 'tooltip','redo', 'ClickedCallback','uiundo(gcbf,''execRedo'')');

Undo/redo buttons

Undo/redo buttons

In the preceding screenshot, since no figure toolbar was previously shown, uipushtool added the undo and redo buttons to a new toolbar. Had the figure toolbar been visible, then the buttons would have been added to its right end. Since undo/redo buttons are normally requested near the left end of toolbars, we need to rearrange the toolbar buttons:

hToolbar = findall(hFig,'tag','FigureToolBar');
%hToolbar = get(hUndo,'Parent');  % an alternative
hButtons = findall(hToolbar);
set(hToolbar,'children',hButtons([4:end-4,2,3,end-3:end]));
set(hUndo,'Separator','on');

Undo/redo buttons in their expected positions

Undo/redo buttons in their expected positions

We would normally preserve hUndo and hRedo, and modify their Tooltip and Visible/Enable properties in run-time, based on the availability and name of the latest undo/redo actions:

% Retrieve redo/undo object
undoObj = getappdata(hFig,'uitools_FigureToolManager');
if isempty(undoObj)
   undoObj = uitools.FigureToolManager(hFig);
   setappdata(hFig,'uitools_FigureToolManager',undoObj);
end
 
% Customize the toolbar buttons
latestUndoAction = undoObj.CommandManager.peekundo;
if isempty(latestUndoAction)
   set(hUndo, 'Tooltip','', 'Enable','off');
else
   tooltipStr = ['undo' latestUndoAction.Name];
   set(hUndo, 'Tooltip',tooltipStr, 'Enable','on');
end

We can easily adapt the method I have just shown to modify/update existing toolbar icons: hiding/disabling them etc. based on the application needs at run-time.

Adding non-button toolbar components - undo dropdown

A more advanced customization is required if we wish to present the undo/redo actions in a drop-down (combo-box). Unfortunately, since Matlab only enables adding uipushtools and uitoggletools to toolbars, we need to use a Java component. The drawback of using such a component is that it is inaccessible via the toolbar’s Children property (implementation of the drop-down callback function is left as an exercise to the reader):

% Add undo dropdown list to the toolbar
jToolbar = get(get(hToolbar,'JavaContainer'),'ComponentPeer');
if ~isempty(jToolbar)
   undoActions = get(undoObj.CommandManager.UndoStack,'Name');
   jCombo = javax.swing.JComboBox(undoActions(end:-1:1));
   set(jCombo, 'ActionPerformedCallback', @myUndoCallbackFcn);
   jToolbar(1).add(jCombo,5); %5th position, after printer icon
   jToolbar(1).repaint;
   jToolbar(1).revalidate;
end
 
% Drop-down (combo-box) callback function
function myUndoCallbackFcn(hCombo,hEvent)
   itemIndex = get(hCombo,'SelectedIndex');  % 0=topmost item
   itemName  = get(hCombo,'SelectedItem');
   % user processing needs to be placed here
end

Undo dropdown list

Undo dropdown list

Note that the javax.swing.JComboBox constructor accepts a cell-array of strings (undoActions in the snippet above). A user-defined dropdownlist might be constructed as follows (also see a related CSSM thread):

...
dropdownStrings = {'here', 'there', 'everywhere'};
jCombo = javax.swing.JComboBox(dropdownStrings);
set(jCombo, 'ActionPerformedCallback', @myUndoCallbackFcn);
jToolbar(1).addSeparator;
jToolbar(1).add(jCombo);  % at end, following a separator mark
jToolbar(1).repaint;
jToolbar(1).revalidate;
...

A similar approach can be used to add checkboxes, radio-buttons and other non-button controls.

In next week’s post I will describe how the toolbar can be customized using undocumented functionality to achieve a non-default background, a floating toolbar (”palette”) effect and other interesting customizations. If you have any specific toolbar-related request, I’ll be happy to hear in the comments section below.

Bookmark and Share