Archive for July, 2009

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)


Non-textual editor actions

Friday, July 17th, 2009

Following my EditorMacro post a couple of weeks ago, which showed how to assign a keyboard macro to the integrated Matlab Editor, several people have asked me whether it is possible to assign a macro to non-textual actions, in addition to the text insertion/replacement which EditorMacro supports.

The quick answer is yes, with some careful programming. Instead of specifying the end result, I will use this opportunity to illustrate how Java objects (not just the editor) can be inspected for their supported actions/properties.

Our first step is to get the requested Java reference handle. This can be done via the Matlab Command Window (interested readers can look at the EditorMacro.m source code, specifically at its getJEditor() function). However, a much easier way is to assign some macro using EditorMacro and then simply place a breakpoint in EditorMacro’s keyPressedCallback() callback function. Then press an arrow key (or any other key) in the editor, and wait for the breakpoint focus to arrive (don’t forget to clear the breakpoint…). From here on, all our actions will be done in the Command Window.

We now have a variable called jEditorPane, which is a reference to a Java object of type javahandle_withcallbacks. com.mathworks.mde.editor.EditorSyntaxTextPane (in Matlab 7 – it’s something similar in Matlab 6). This is a Matlab wrapper for the basic Java object, used for accessing the callback hooks, as explained in a previous post. In our case we are not interested in this wrapper but in its wrapped object, which is retrieved via Matlab’s built-in java function (java(jEditorPane) or jEditorPane.java). The inspection itself is done using Matlab’s standard tools (inspect, methodsview etc.) or via my UIINSPECT utility. I suggest using UIINSPECT, which displays all the information of the standard tools and lots extra, but I’m of course biased…

uiinspect(jEditorPane.java);

jEditorPane inspection using UIINSPECT (click to see details)

jEditorPane inspection using UIINSPECT (click to see details)

Without diving into all the UIINSPECT options (I shall do this in a dedicated post), we see the supported methods/actions on the left, and properties on the right. It is true that none of them are documented, but many are self-explanatory. For example, the cut()/copy()/paste() methods or the caretPosition/caretColor properties. Any combination of these methods and properties can be used in a user-defined macro.

Let’s do a simple example, setting the <Ctrl-E> combination to a macro moving to the end-of-line (unix-style – equivalent to <End> on Windows), and <Ctrl-Shift-E> to a similar macro doing the same while also selecting the text (like <Shift-End> on Windows). We shall even use the same macro code, by simply checking in the eventData whether the <Shift> key is depressed:

function EOL_Macro(hDocument,eventData)
 
  % Find the position of the next EOL mark
  currentPos = hDocument.getCaretPosition;
  docLength = hDocument.getLength;
  textToEOF = char(hDocument.getTextStartEnd(currentPos,docLength));
  nextEOLPos = currentPos+find(textToEOF<=13,1)-1;  % next CR/LF pos
  if isempty(nextEOLPos)
      % no EOL found (=> move to end-of-file)
      nextEOLPos = docLength;
  end
 
  % Do action based on whether <Shift> was pressed or not
  %get(eventData);
  if eventData.isShiftDown
      % Select to EOL
      hDocument.moveCaretPosition(nextEOLPos);
  else
      % Move to EOL (without selection)
      hDocument.setCaretPosition(nextEOLPos);
  end
 
end  % EOL_Macro

…and now let’s activate this macro in the Matlab Command Window:

>> macros = EditorMacro('ctrl-e',@EOL_Macro,'run');
>> macros = EditorMacro('ctrl-shift-e',@EOL_Macro,'run')
macros = 
    'ctrl alt pressed T'      @(a,b)datestr(now)    'text'
    'ctrl pressed E'          @EOL_Macro            'run' 
    'shift ctrl pressed E'    @EOL_Macro            'run'

Please do explore all the possible actions/properties exposed by the jEditorPane object. Probably the worst that could happen (and very rarely) is that you’ll crash Matlab and need to restart it – no biggy. If you find an interesting macro combination, please post it to the File Excahnge, and/or let us all know by placing a comment below.

BlinkDagger – the end?

Wednesday, July 8th, 2009

In his latest post on BlinkDagger, Quan Quach announced that the BlinkDagger blog will be frozen following co-author Daniel Sutoyo’s hiring by The MathWorks and the continuous strain of maintaining the blog single-handedly.

This is sad news indeed for the Matlab user community. Over the past few years, BlinkDagger has become the largest independent Matlab-related blog (at least that I know of), with some 150 well-written tutorials on a very wide variety of Matlab-related topics. In fact, I believe that only the official Matlab blogs and the Matlab CSSM forum contain more Matlab content. Although there are reportedly over a million Matlab users worldwide, there is precious little independent online Matlab content. Perhaps the need is answered by CSSM and the official blogs, and perhaps the reason is other. In any case, BlinkDagger’s retirement will be well-felt.

As a novice (4-month) blogger I can well appreciate the effort it took Quan and Daniel to write so much well-crafted content, follow-up on numerous comments and continuously think of interesting article ideas. The fact they have succeeded in doing so consistently for so long, without noticeable quality degradation, is remarkable. So, while regretting their action, I can certainly understand it. I truly wish both Quan and Daniel the best of luck in their new adventures, and send them an enormous hug of gratitude for their services to the Matlab community.

Don’t be lazy – be a sport – visit the BlinkDagger link and tell Quan and Daniel that you too appreciate their efforts. Let them leave the stage with a standing ovation rather than a whimper. They deserve it.

Yair Altman

EditorMacro – assign a keyboard macro in the Matlab editor

Wednesday, July 1st, 2009

Over the past years there have been quite a few requests to enable keyboard macros and keybinding modifications in the Matlab editor. Some posters have even noted this lack as their main reason to use an external editor. Coincidentally, some weeks ago I was approached by a reader (Grant Roch) to help with assigning some textual editor macros, and with joint work we were able to figure out a basic working mechanism. The latest user comment on this issue was posted on the official Matlab Desktop blog yesterday. All this prompted me to finally post a solution to this need: My EditorMacro utility is now available for download on the Mathworks File Exchange. In this post I will detail some of its inner workings, which rely on undocumented Matlab features.

In a nutshell, EditorMacro sets the KeyPressedCallback property (explained in a previous post) for each of the editor’s document panes, to an internal function. This internal function then checks each keystroke against a list of registered keybindings. The list itself is persisted in the editor object’s hidden ApplicationData property (accessible via the getappdata/setappdata built-in functions). If a match is found, then the associated macro is invoked. Depending on the macro type, some text can be inserted at the current editor caret position, or replacing the selected editor text, or a non-text Matlab function/command can be invoked.

This enables EditorMacro to be used for quickly inserting code templates (header comments, try-catch blocks etc.) or for automating Matlab unit testing.

Here’s a typical usage example: start by defining a simple function that returns a dynamic header comment:

function comment = createHeaderComment(hDocument, eventData)
  timestamp = datestr(now);
  username = getenv('username');
  %computer = getenv('computername');  % unused
  lineStr = repmat('%',1,35);
  comment = sprintf(...
      ['%s\n' ...
       '%% \n' ...
       '%% Name:    functionName\n' ...
       '%% \n' ...
       '%% Desc:    enter description here\n' ...
       '%% \n' ...
       '%% Inputs:  enter inputs here\n' ...
       '%% \n' ...
       '%% Outputs: enter outputs here\n' ...
       '%% \n' ...
       '%% Created: %s\n' ...
       '%% \n' ...
       '%% Author:  %s\n' ...
       '%% \n' ...
       '%s\n'], ...
       lineStr, timestamp, username, lineStr);
end  % createHeaderComment

Now define a macro to use this function, and another simple try-catch template macro:

>> EditorMacro('Alt-Control-h', @createHeaderComment);
>> macroStr = 'try\n  % Main code here\ncatch\n  % Exception handling here\nend';
>> b = EditorMacro('Ctrl alt T', macroStr)
b = 
    'ctrl alt pressed H'    @createHeaderComment    'text'
    'ctrl alt pressed T'             [1x60 char]    'text'

Now start with a blank document and click <Ctrl>-<Alt>-H and <Ctrl>-<Alt>-T. This is the end result:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 
% Name:    functionName
% 
% Desc:    enter description here
% 
% Inputs:  enter inputs here
% 
% Outputs: enter outputs here
% 
% Created: 01-Jul-2009 23:31:46
% 
% Author:  Yair Altman
% 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
try
  % Main code here
catch
  % Exception handling here
end

Keybindings are normalized using Java’s built-in javax.swing.KeyStroke.getKeyStroke() method, to enable the user a very wide range of keystroke naming formats (e.g., ‘Alt-Control-T’ or ‘ctrl alt t’).

I have taken great pains to make EditorMacro compatible with all Matlab versions since 6.0 (R12). This was no easy feat: Matlab 7 made some significant changes to the editor layout. Luckily, once I found out how to get a handle to the Matlab 6 editor object (this took some hours of trial-and-error), listing its display hierarchy was simple and the modifications were generally straight-forward, although non-trivial (different quirks due to missing default type-castings, missing eventData in invoked callbacks etc…). Please feel free to look at EditorMacro’s source code for the clearly-marked Matlab 6 segments.

For the record, here is a snippet showing how to get the editor object in Matlab 6 and 7. Note that the desktop handle is only useful to get the editor handle in Matlab 6 – we need a totally different way in Matlab 6:

try
   % Matlab 7
   jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
   jEditor = jDesktop.getGroupContainer('Editor');
catch
   % Matlab 6
   try
      %desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop;
      % no use: can't get to the editor from the desktop handle in Matlab 6...
      openDocs = com.mathworks.ide.editor.EditorApplication.getOpenDocuments;
      firstDoc = openDocs.elementAt(0);
      jEditor = firstDoc.getParent.getParent.getParent;
   catch
      error('Cannot find Matlab editor handle: possibly no open documents');
   end
end

Another complication arose due to the different layout used for floating/maximized/tiled document layout in the editor. Yet another was due to the different behavior (at least on Matlab 6) between a one-document and a multi-document editor view.

Due to the way keyboard events are processed by the Matlab editor, KeyPressedCallback needs to be set separately for all the open document panes and split-panes. Since we also wish newly-opened documents to recognize the macro bindings, we need to set the common container ancestor’s ComponentAddedCallback to an internal function that will handle the KeyPressedCallback instrumentation for each newly-opened document. Again, this needs to be done somewhat differently for Matlab 6/7.

Note that EditorMacro relies on the editor’s internal display layout, which is very sensitive to modification between Matlab releases (as it has between Matlab 6 and 7, for example).

All-in-all, I believe EditorMacro provides a useful and long-awaited service, which should in fact be part of the built-in editor.