Archive for June, 2009

Setting the Matlab desktop layout programmatically

Wednesday, June 24th, 2009

The Matlab desktop enable users to switch between different presentation layouts of the desktop panels (Command Window, Workspace etc.). This has been supported as far back as Matlab 6 (R12), with newer Matlab releases adding improved functionality such as the ability to save user-defined layouts, as Kristin explained in the official Matlab desktop blog.

The only supported way to save and switch layouts is to use the desktop’s main menu. Since Kristin has posted her write-up, a few people have posted unanswered follow-up comments requesting to know how to programmatically save and switch layouts. I will now show how this can be done.

First, we need to get the Java handle of the Matlab desktop. We can then investigate this handle using the built-in methodsview function or my UIInspect utility on the File Exchange. We quickly see the relevant layout-related functions, which we can put to good use:

% Get the desktop's Java handle (Matlab 7 only)
desktop = com.mathworks.mde.desk.MLDesktop.getInstance;
 
% Inspect the available desktop functions
methodsview(desktop);
uiinspect(desktop);
 
% Save the current layout
desktop.saveLayout('Yair');
 
% Switch between different layouts
desktop.restoreLayout('Yair');
desktop.restoreLayout('Default');
desktop.restoreLayout('History and Command Window');

Desktop layout menu in Matlab 7

Desktop layout menu in Matlab 7

Note that trying to restore an invalid layout name simply does nothing (does not throw an error).

Also note that this relies heavily on unsupported and undocumented internal implementation, which may change without prior notice between Matlab releases. The code snippet above works on several Matlab 7 releases. But for Matlab 6.0 (R12), for example, it needs to be modified:

% Get the desktop's Java handle (Matlab 6 only)
desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop;
 
% Inspect the available desktop functions
% Note: in Matlab 6, methodsview() did not accept object handles
methodsview('com.mathworks.ide.desktop.MLDesktop');
%uiinspect(desktop);  % UIINSPECT does not work on Matlab 6
 
% Save the current layout
% saving the desktop is not possible in Matlab 6
 
% Switch between different layouts
desktop.set5PanelLayout;
desktop.setTallLayout;
desktop.setShortLayout;
desktop.setDefaultDesktop;
desktop.setMolerMode;        % ='Command window only'

Desktop layout menu in Matlab 6

Desktop layout menu in Matlab 6

Note that Matlab 6 did not have a generic restoreLayout() function, instead using a few pre-defined setXXX(). Also note that Matlab 6 did not have the saveLayout() function (it did have saveDesktop(string,string) and restoreDesktop(string,string), which I leave as an excercise to readers).

This has been the first example of many useful things that can be done with the Matlab desktop handle. In the future I will describe other aspects. Perhaps the main lesson to be learned from this post is that essentially anything that can be done via the menu can also be done programmatically.

Changing system preferences programmatically

Wednesday, June 17th, 2009

My very first post on this blog showed how to change Matlab’s command-window colors. In that post I promised to write another post detailing how system preferences can be changed from the command line.

Last week I wrote about undocumented GUIDE features. After my initial post, I realized that I forgot to mention all the different relevant system preferences that can also be modified. I therefore modified last week’s post with the preferences information. I now wish to finally detail how such preferences can be changed programmatically, from within your Matlab application or from the Matlab desktop command prompt.

Matlab’s user preferences are stored in the matlab.prf text file which is stored in the user’s Matlab preferences folder:

edit(fullfile(prefdir,'matlab.prf'));

Each preference appears to be on a separate line in the following format: <pref-name>=<pref-type><pref-value>

where <pref-type> appears to be one of these:

  • ‘B’ => boolean/logical flag
  • ‘C’ => color (RGB numeric value)
  • ‘F’ => font (type,size,name)
  • ‘I’ => int16
  • ‘J’ => int64
  • ‘R’ => rectangular area (x,y,h,w)
  • ‘S’ => string/char

Examples:
    EditorShowLineNumbers=Btrue
    EditorMaxCommentWidth=I120

You can read the preference names from this matlab.prf file and then use the following (you-guessed-it) java calls to get/set the values:

com.mathworks.services.Prefs.get<type>Pref(<pref-name>)
com.mathworks.services.Prefs.set<type>Pref(<pref-name>, newValue);

where <type> is one of: Boolean, Color, RGBColor, Font, Integer, Rectangle, String, Double (I believe Doubles get converted to int64 – possibly a bitwise casting since both use 64 bits)

For example:

com.mathworks.services.Prefs.getBooleanPref('LayoutSnapToGrid')
com.mathworks.services.Prefs.setIntegerPref('LayoutGridWidth', 25)

adding a second argument to get<type>Pref() appears to indicate a default value that is returned if <pref-name> is not defined:

com.mathworks.services.Prefs.getIntegerPref('xxxx',123)
=> 123

We can programmatically set any preference key we like – we are not limited to Matlab’s built-in set. I used this feature in my CPRINTF utility, to set user-defined colors for later use by the desktop’s UI syntax-highlighting engine. The relevant code segment is this:

% Convert a Matlab RGB vector into a known style name (e.g., '[255,37,0]')
function styleName = getColorStyle(rgb)
 
  % Convert Matlab RGB array into a Java Color object
  intColor = int32(rgb*255);
  javaColor = java.awt.Color(intColor(1), intColor(2), intColor(3));
 
  % Preference key name format: '[RRR,GGG,BBB]'
  styleName = sprintf('[%d,%d,%d]',intColor);
 
  % Set/update the preference with this Java Color
  com.mathworks.services.Prefs.setColorPref(styleName,javaColor);

…which in turn adds entries such as the following to my matlab.prf file:
    [12,34,67]=C-15982013

Note that -15982013 = 0xFF0C2243, which is the RGB value [12,34,67] with an opaque alpha value. This value can later be retrieved using:

>> com.mathworks.services.Prefs.getColorPref('[12,34,67]')
ans =
java.awt.Color[r=12,g=34,b=67]

Warning: I published much of this information on the CSSM forum back in 2007. Ben Steiner then shared his experience on that thread that:

For anyone else that’s playing with this: I don’t advise trying to edit the matlab.prf via matlab(!). I created a situation that made Matlab unworkable. I did find that deleting the matlab.prf completely (in frustration) solved it.

Take a look at your matlab.prf file – can you spot any interesting preference? If so, please share it in the comments section below.

GUIDE customization

Wednesday, June 10th, 2009

GUIDE is the acronym for Matlab’s Graphical User Interface Design Editor. It is very handy for designing simple GUI figures, although my experience has shown that it has limitations for complex GUIs. Nevertheless, GUIDE is the tool used by most Matlab developers when designing GUIs. In this post, I will show a few undocumented customizations that could help make GUIDE sessions more productive.

The starting point is GUIDE’s undocumented return value, which is a Java reference to the Layout Editor panel within the GUIDE figure frame:

>> h = guide
h =
Layout Document [untitled]

>> h.getClass
ans =
class com.mathworks.toolbox.matlab.guide.LayoutEditor

This return handle can be used to access GUIDE components and functionality. We can start by inspecting the interesting GUIDE layout hierarchy using my FindJObj utility, and the associated properties and method using my UIInspect utility:

>> h.findjobj;
>> h.uiinspect;

Hierarchy of Layout Editor within the GUIDE frame

Hierarchy of Layout Editor within the GUIDE frame

Note: If you wish to see the hierarchy of the entire GUIDE figure frame, simply run FindJObj on the frame reference, by either of the two following methods (and similarly for UIInspect):

>> findjobj(h.getFrame);
>> findjobj(h.getTopLevelWindow);

We see that the Layout Editor contains, in addition to the expected LayoutArea and two MWScrollbars, several objects that relate to a ruler. These rulers can be activated via the GUIDE menu (Tools / Grid and Rulers), or via the Matlab Command Prompt as described below:

Looking at the ruler properties in FindJObj or UIInspect, we can see a settable boolean property called “RulerState”. If we turn it on we can see that a very handy pixels-ruler appears. Once we set this property, it remains in effect for every future GUIDE session:

Before: GUIDE with no rulers

Before: GUIDE with no rulers

h.getComponent(0).getComponent(4).setRulerState(true);  % Horizontal
h.getComponent(0).getComponent(5).setRulerState(true);  % Vertical

Note: RulerState actually controls a system preference (LayoutShowRulers, a boolean flag) that controls the visibility of both rulers, and persists across Matlab/GUIDE sessions. To change the visibility of only a single ruler for this GUIDE session only, or on old Matlab versions (e.g. Matlab 7.1 aka R14 SP3) that do not have the ‘RulerState’ property, use the hide()/show()/setVisible(flag) methods, or set the ‘Visible’ property:

% Equivalent ways to show horizontal ruler for this GUIDE session only
hRuler = h.getComponent(0).getComponent(4);  % =top horizontal ruler
set(hRuler, 'Visible','on');
hRuler.setVisible(true);  % or: hRuler.setVisible(1)
hRuler.show();

After: GUIDE with pixel rulers

After: GUIDE with pixel rulers

Using this method, we can customize the rulers – options which are unavailable using the standard GUIDE menu options: We can specify horizontal/vertical grid size, tick & label interval and similar ruler properties. For example, let’s set a 5-pixel minor tick interval, 25-pixel major interval, labels every 50 pixels, starting offset of 40 pixels and a ruler size limited at 260 pixels:

hRuler = h.getComponent(0).getComponent(4);  % =top horizontal ruler
set(hRuler, 'MinorInterval',5, 'MajorInterval',25);
set(hRuler, 'LabelInterval',50, 'LabelUnit',50);
set(hRuler, 'Margin',40, 'Length',260);

GUIDE with modified pixel rulers

GUIDE with modified pixel rulers

Note that the vertical ruler’s labels start (=LabelStart property) at the figure’s height, and have a decreasing LabelInterval of -50. This is done because Java coordinates start counting from the top-left corner downward, whereas Matlab counts from the bottom-left upward. In GUIDE, we naturally wish to display the Matlab coordinates, hence the transformation.

Note: unfortunately, most of these properties do not have equivalent settable system properties that I could find. Here is a list of all the GUIDE-related system properties that I found:

  • LayoutShowRulers – boolean, controls display of both rulers
  • LayoutShowGuides – boolean, controls display of blue guidelines
  • LayoutShowGrid – boolean, controls display of gray gridlines
  • LayoutGridWidth – integer, controls the size of the grid boxes
  • LayoutSnapToGrid – boolean, controls snap-to-grid behavior
  • LayoutActivate – boolean, controls ability to run (activate) unsaved figures without confirmation
  • LayoutChangeDefaultCallback – boolean, ??? (I can see this preference in my matlab.prf file but I have no idea what it does or how it got there…)
  • LayoutExport – boolean, controls ability to export unsaved figures without confirmation
  • LayoutExtension – boolean, controls display of file extension in the GUIDE window title
  • LayoutFullPath – boolean, controls display of file path in the GUIDE window title
  • LayoutMCodeComments – boolean, controls generation of comments for m-file callbacks
  • LayoutToolBar – boolean, controls display of the GUIDE widow toolbar
  • LayoutToolNames – boolean, controls display of tool names in the components palette

Have you discovered other undocumented features in GUIDE? If so, please share your findings in the comments section below.

Warning: These undocumented features are way deep in unsupported territory. They depend heavily on Matlab’s internal implementation, which may change without any prior notice between Matlab releases. They work ok on Matlab versions 7.1 (R14 SP3) through 7.6 (R2008a), and perhaps on other versions as well. However, the very next Matlab release might break these features, so beware.

Legend ‘-DynamicLegend’ semi-documented feature

Thursday, June 4th, 2009

In one of my projects, I had to build a GUI in which users could interactively add and remove plot lines from an axes. The problem was that the legend needed to be kept in constant sync with the currently-displayed plot lines. This can of course be done programmatically, but a much simpler solution was to use legend’s semi-documented ‘-DynamicLegend’ feature. Here’s a simple example:

x=0:.01:10;
plot(x, sin(x), 'DisplayName','sin');
legend('-DynamicLegend');
 
hold all;   % add new plot lines on top of previous ones
plot(x, cos(x), 'DisplayName','cos');

We can see how the dynamic legend automatically keeps in sync with its associated axes contents when plot lines are added/removed, even down to the zoom-box lines… The legend automatically uses the plot lines ‘DisplayName’ property where available, or a standard ‘line#’ nametag where not available:


Dynamic legend

Dynamic legend

DynamicLegend works by attaching a listener to the axes child addition/deletion callback (actually, it works on the scribe object, which is a large topic for several future posts). It is sometimes necessary to selectively disable the dynamic behavior. For example, in my GUI I needed to plot several event lines which looked alike, and so I only wanted the first line to be added to the legend. To temporarily disable the DynamicLegend listener, do the following:

% Try to disable this axes's legend plot-addition listener
legendAxListener = [];
try
   legendListeners = get(gca,'ScribeLegendListeners');
   legendAxListener = legendListeners.childadded;
   set(legendAxListener,'Enable','off');
catch
   % never mind...
end
 
% Update the axes - the legend will not be updated
...
 
% Re-enable the dynamic legend listener
set(legendAxListener,'Enable','on');

Unfortunately, this otherwise-useful DynamicLegend feature throws errors when zooming-in on bar or stairs graphs. This can be replicated by:

figure;
bar(magic(4));  %or: stairs(magic(3),magic(3));
legend('-DynamicLegend');
zoom on;
% Now zoom-in using the mouse to get the errors on the Command Window

The fix: modify %MATLABROOT%\toolbox\matlab\scribe\@scribe\@legend\init.m line #528 as follows:

%old:
str = [str(1:insertindex-1);{newstr};str(insertindex:length(str))];
 
%new:
if size(str,2)>size(str,1)
    str=[str(1:insertindex-1),{newstr},str(insertindex:length(str))];
else
    str=[str(1:insertindex-1);{newstr};str(insertindex:length(str))];
end

The origin of the bug is that bar and stairs generate hggroup plot-children, which saves the legend strings column-wise rather than the expected row-wise. My fix solves this, but I do not presume this solves all possible problems in all scenarios (please report if you find anything else).

Semi-documented

The DynamicLegend feature is semi-documented. This means that the feature is explained in a comment within the function (which can be seen via the edit(‘legend’) command), that is nonetheless not part of the official help or doc sections. It is an unsupported feature originally intended only for internal Matlab use (which of course doesn’t mean we can’t use it). This feature has existed many releases back (Matlab 7.1 for sure, perhaps earlier), so while it may be discontinued in some future Matlab release, it did have a very long life span… The down side is that it is not supported: I reported the bar/stairs issue back in mid-2007 and so far this has not been addressed (perhaps it will never be). Even my reported workaround in January this year went unanswered (no hard feelings…).

DynamicLegend is a good example of a useful semi-documented feature. Some other examples, which I may cover in future posts, include text(…,’sc’), drawnow(‘discard’), several options in pan and datacursormode etc. etc.

There are also entire semi-documented functions: many of the uitools (e.g., uitree, uiundo), as well as hgfeval and others.

Have you discovered any useful semi-documented feature or function? If so, then please share your finding in the comments section below.