Posts Tagged ‘JavaFrame’

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.

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…

Setting status-bar components

Thursday, July 30th, 2009

I last week’s post, Setting status-bar text, I showed how to set the status-bar text in Matlab figures and the main desktop. But Matlab status-bars are Java containers in which we can add GUI controls, not just simple text labels. In this post I will show how to do this for Matlab 7 figures.

Let’s return to the two alternatives I’ve presented in my previous post for setting a Matlab 7 figure status-bar text:

% Alternative #1 (hFig = requested figure's handle)
jFrame = get(hFig,'JavaFrame');
jFigPanel = get(jFrame,'FigurePanelContainer');
jRootPane = jFigPanel.getComponent(0).getRootPane;
jRootPane = jRootPane.getTopLevelAncestor;
statusbarObj = jRootPane.getStatusBar;
statusbarObj.setText(statusText);
jRootPane.setStatusBarVisible(1);
 
% Alternative #2
jFrame = get(hFig,'JavaFrame');
jRootPane = jFrame.fFigureClient.getWindow;
statusbarObj = com.mathworks.mwswing.MJStatusBar;
jRootPane.setStatusBar(statusbarObj);
statusbarObj.setText(statusText);

The first alternative uses the default status-bar (a com.mathworks.mwswing.MJStatusBar object) which Matlab automatically creates with each figure. This object is created as invisible, so we need to set its text (via its setText method), and then make it visible (via its ancestor’s root-pane’s setStatusBarVisible method – setting the object itself to visible is not enough). The alternative is to replace the default status-bar object with a user-specified container (in this case, we use a new instance of com.mathworks.mwswing.MJStatusBar).

Last week I forgot to mention that only the second alternative works in the general case – it appears that the first alternative often fails because figures are not created with the default statusbar on many platforms/Matlab release (I’m not sure exactly which). My StatusBar utility on the MathWorks File Exchange, which I mentioned in last week’s post, takes care of these nuances, and automatically creates the status bar object if it is missing.

In any case, using these two code snippets as a baseline, we can customize any Java container that we wish. We are not limited to text labels. The default ststusbar container, MJStatusBar, only includes a single JLabel-derived component that stores the text message. We can add other components to this container. For example, let’s add a simple progress-bar:

jFrame = get(hFig,'JavaFrame');
jRootPane = jFrame.fFigureClient.getWindow;
statusbarObj = com.mathworks.mwswing.MJStatusBar;
 
% Add a progress-bar to left side of standard MJStatusBar container
jProgressBar = javax.swing.JProgressBar;
set(jProgressBar, 'Minimum',0, 'Maximum',500, 'Value',234);
statusbarObj.add(jProgressBar,'West');  % 'West' => left of text; 'East' => right
% Beware: 'East' also works but doesn't resize automatically
 
% Set this container as the figure's status-bar
jRootPane.setStatusBar(statusbarObj);
 
% Note: setting setStatusBarVisible(1) is not enough to display the status-bar
% - we also need to call setText(), even if only with an empty string ''
statusbarObj.setText('testing 123...');
jRootPane.setStatusBarVisible(1);


Status bar with a simple progress-bar

Status bar with a simple progress-bar


We can of course use the progress-bar and status-bar handles to modify their appearance within our code, for example within some loop. For example:

numIds = length(allIds);
set(jProgressBar, 'StringPainted','on', 'Maximum',numIds, 'Value',0);
for id = 1 : numIds
 
   % Update status bar
   set(jProgressBar, 'StringPainted','on', 'Value',id);
   msg = 'Processing %d of %d (%.1f%%)...';
   statusbarObj.setText(sprintf(msg,id,numIds,100*id/numIds));
 
   % do something useful...
 
end  % for all Ids
 
% Some final status-bar updates...
% Hide the progress-bar
jProgressBar.setVisible(0);
 
% Hide the corner grip
cornerGrip = statusbarObj.getParent.getComponent(0);
cornerGrip.setVisible(0);  % or: set(cornerGrip,'Visible','off')
 
% Set a red foreground & yellow background to status bar text
statusbarObj.setText('All done - congratulations!!!');
statusbarTxt = statusbarObj.getComponent(0);
statusbarTxt.setForeground(java.awt.Color.red);
set(statusbarTxt,'Background','yellow');
set(statusbarTxt,'Background',[1,1,0]);  % an alternative…


Modifying status bar properties in run-timer

Modifying status bar properties in run-time


The progress-bar component was just a simple example of how to present non-intrusive controls/information in the figure status bar. Other controls (buttons, checkboxes etc.) can similarly be added.

Note that the status-bar’s corner-grip (at its far right) is not a sub-component of the statusbarObj object like the label, but rather of its parent JPanel container. This is easily be seen using my FindJObj utility on the MathWorks File Exchange:


Status bar with a simple progress-bar

Status bar with a simple progress-bar


One final note: The status bar is 20 pixels high across the entire bottom of the figure. It hides everything between pixel heights 0-20, even parts of uicontrols, regardless of who was created first or the relative ComponentZOrder in the frame’s ContentPane:

% Add a "Next phase" button to the right of the text
jb = javax.swing.JButton('Next phase >');
jbh = handle(jb,'CallbackProperties');
set(jbh, 'ActionPerformedCallback', @nextPhaseFunction);
statusbarObj.add(jb,'East');
%note: we might need jRootPane.setStatusBarVisible(0)
% followed by jRootPane.setStatusBarVisible(1) to repaint
 
% Add a simple Matlab uicontrol, obscured by the status-bar
hb = uicontrol('string','click me!', 'position',[10,15,70,30]);


Adding controls inside and outside the status-bar

Adding controls inside and outside the status-bar


If you have made interesting use of Matlab’s status-bar, please share them in the comments section below.

Setting status-bar text

Thursday, July 23rd, 2009

Most GUI windows nowadays, ranging from web browsers to editors and data-entry windows, have a small status-bar at their bottom. Statusbars are a great way to convey non-critical information to the user, in a non-intrusive manner (without popup windows).

Matlab windows are no exception: they too have status bars which are sometimes used by internal Matlab functions. Unfortunately, there is no documented way to modify Matlab statusbars. In this post I will demonstrate the undocumented way of setting status-bar text, which is probably the most useful use-case. In next week’s post, I will expand this to include more complex customizations, such as dynamic progress-bars or a corner grip.

Note: Users interested in non-intrusive status-bar messages, may also be interested in another non-intrusive messaging mechanism which I have described some months ago: Setting system tray popup messages.

Desktop window status-bar

We start with the simplest case of setting the desktop’s statusbar text. We have no further to go than the bottom of Matlab’s own uiopen (see %matlabroot%/toolbox/matlab/uitools/uiopen.m):

dt = javaMethod('getInstance', 'com.mathworks.mde.desk.MLDesktop');
if dt.hasMainFrame
    dt.setStatusText(message);
else
    disp(message);
end

Basically, we try to get the Java reference to the Matlab desktop, see if it has an open window frame and if so then we use its setStatusText() method to display a text message; if not then we display the message in the Command Window (which is probably a non-window console).

Unfortunately, if we place this code in our m-files as-is, our status message will be overridden by Matlab’s “busy” message. A solution for this is to use a one-time (discardable) timer. Another complication is meant for supporting Matlab 6, in which we get the desktop reference differently, and which did not yet have the timer function. The final code now looks like this:

%% Set the status bar text of the Matlab desktop
function setDesktopStatus(statusText)
  % First, get the desktop reference
  try
    desktop = com.mathworks.mde.desk.MLDesktop.getInstance;     % Matlab 7+
  catch
    desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop; % Matlab 6
  end
 
  if desktop.hasMainFrame
    % Schedule a timer to update the status text
    % Note: can't update immediately (will be overridden by Matlab's 'busy' message)
    try
      timerFcn = {@setText,desktop,statusText};
      t = timer('TimerFcn',timerFcn, 'StartDelay',0.05, 'ExecutionMode','singleShot');
      start(t);
    catch
      % Probably an old Matlab version that still doesn't have timer
      desktop.setStatusText(statusText);
    end
  else
    disp(statusText);
  end
 
%% Utility function used as setDesktopStatus's internal timer's callback
function setText(varargin)
  if nargin == 4  % just in case...
    targetObj  = varargin{3};
    statusText = varargin{4};
    targetObj.setStatusText(statusText);
  else
    % should never happen...
  end

Figure window status-bar

Setting the status-bar text on figure windows is slightly more difficult: getstatus and setstatus appear to be early attempts by Matlab to enable users an access to a figure’s statusbar. In this early attempt, Matlab assumes that the user prepares a text label having a tag of ’status’. getstatus then returns this label’s string, while setstatus modifies it:

uicontrol('Parent',gcf,'Style','text','Tag','Status');
setstatus(gcf, 'Goodbye');
string = getstatus(gcf);

Unfortunately, nothing prevents the user from placing the label anywhere in the figure, and also from having multiple such labels at once, adding to the confusion. The result is inconsistent with normal windowing practices, and this is probably the reason that MathWorks have grandfathered these functions in Matlab 7.4 (R2007a). It would be much more logical for Matlab to have the statusbar accessible via a figure property, and perhaps this will happen in some future version.

A better, consistent and more flexible access to the figure statusbar can be achieved by using some undocumented Java functions. In a nutshell, we get the figure’s JavaFrame property (which Matlab has warned might be discontinued in some near upcoming release), which is sort of a handle reference to the Java window underlying the Matlab figure window (not exactly but close enough for today). We then travel up and down the JavaFrame window objects hierarchy (use my FindJObj utility on the File Exchange to understand this hierarchy) until we get to the status bar object. We then set its text and make it visible (it’s invisible by default). Here is a trimmed-down version (excluding the necessary sanity checks, exception handling etc.):

% Alternative #1 (hFig = requested figure's handle)
jFrame = get(hFig,'JavaFrame');
jFigPanel = get(jFrame,'FigurePanelContainer');
jRootPane = jFigPanel.getComponent(0).getRootPane;
jRootPane = jRootPane.getTopLevelAncestor;
statusbarObj = jRootPane.getStatusBar;
statusbarObj.setText(statusText);
jRootPane.setStatusBarVisible(1);
 
% Alternative #2
jFrame = get(hFig,'JavaFrame');
jRootPane = jFrame.fFigureClient.getWindow;
statusbarObj = com.mathworks.mwswing.MJStatusBar;
jRootPane.setStatusBar(statusbarObj);
statusbarObj.setText(statusText);


simple figure status-bar text message

simple figure status-bar text message


StatusBar utility

I’ve created a wrapper function, aptly called statusbar, encapsulating all the above with some additional error checking etc. I have posted statusbar on the MathWorks File Exchange. Readers are encouraged to look at this submission’s source code for examples of statusbar manipulation. Here are several usage examples:


statusbar usage examples (click to see details)

statusbar usage examples (click to see details)


Displaying hidden handle properties

Tuesday, May 5th, 2009

Matlab Handle Graphics (HG) is a great way to manipulate GUI objects. HG handles often have some undocumented hidden properties. One pretty well-known example is the JavaFrame property of the figure handle, which enables access to the GUI’s underlying Java peer object. We can use hidden properties just like any other handle property, using the built-in get and set functions.

But how can we know about these properties? Here are two methods to do so. Like the hidden properties, these two methods are themselves undocumented…

1. use the desktop’s hidden HideUndocumented property:

set(0,'HideUndocumented','off');

From now on, when displaying handle properties using get and set you’ll see the hidden properties.

Note that some of the properties might display a warning indication:

>> get(gcf)
	Alphamap = [ (1 by 64) double array]
	BackingStore = on
	CloseRequestFcn = closereq
	Color = [0.8 0.8 0.8]
	Colormap = [ (64 by 3) double array]
	CurrentAxes = []
	CurrentCharacter =
	CurrentKey =
	CurrentModifier = [ (1 by 0) cell array]
	CurrentObject = []
	CurrentPoint = [0 0]
	DithermapWarning: figure Dithermap is no longer useful
 with TrueColor displays, and will be removed in a future release.
 = [ (64 by 3) double array]
        ...

2. Access the properties’ definition in the handle’s class definition:

>> ch = classhandle(handle(gcf));
>> props = get(ch,'Properties');
>> propsVisibility = get(props,'Visible')';
>> hiddenProps = props(strcmp(propsVisibility,'off'));
>> sort(get(hiddenProps,'Name'))
ans =
    'ALimInclude'
    'ActivePositionProperty'
    'ApplicationData'
    'BackingStore'
    'Behavior'
    'CLimInclude'
    'CurrentKey'
    'CurrentModifier'
    'Dithermap'
    'DithermapMode'
    'ExportTemplate'
    'HelpFcn'
    'HelpTopicKey'
    'HelpTopicMap'
    'IncludeRenderer'
    'JavaFrame'
    'OuterPosition'
    'PixelBounds'
    'PrintTemplate'
    'Serializable'
    'ShareColors'
    'UseHG2'
    'WaitStatus'
    'XLimInclude'
    'YLimInclude'
    'ZLimInclude'

Different HG handles have different hidden properties. Not all these properties are useful. For example, I have found the PixelBounds property to be problematic – (it sometimes reports incorrect values!). Other properties (like Dithermap or ShareColors) are deprecated and display a warning wherever they are accessed.

But every so often we find a hidden property that can be of some actual benefit. Let’s take the figure handle’s OuterPosition property for example. It provides the figure’s external position values, including the space used by the window frame, toolbars etc., whereas the regular documented Position property only reports the internal bounds:

>> get(gcf,'pos')
ans =
   232   246   560   420
>> get(gcf,'outer')
ans =
   228   242   568   502

In future posts I will sometimes use such hidden properties. You can find the latest list by looking at this blog’s “Hidden property” category page.

Note: Like the rest of Matlab’s undocumented items, all hidden properties are undocumented, unsupported and may well change in future Matlab releases so use them with care.

Did you find any useful hidden property? If so, then please leave your finding in the comments section below.