A few weeks ago, Robert Cumming explained how we can use a Matlab uicontrol’s CData property to provide an optical illusion of a transparent background. Today I will discuss another usage of this property, providing a simple checkbox control the unorthodox appearance of a split-pane divider.
The underlying problem description is easy: we wish to have the ability to split a Matlab uipanel into two or more sub-panels, separated by a draggable horizontal/vertical divider. Such split-panes are standard in any semi-decent GUI, but for some reason were never incorporated in official Matlab. This is a real pity, but not to worry as there are at least two alternatives we could use:
UISplitPane is a utility that I wrote back in 2009 that uses a Java
JSplitPane divider and associates it with plain Matlab panels on both sides. This solves the problem of embedding Matlab axes in Java panels, such as the ones provided by the standard Java
JSplitPane. A detailed description of the technique can be found in my dedicated post on this utility.
[hDown,hUp,hDiv1] = uisplitpane(gcf, 'Orientation','ver', 'dividercolor',[0,1,0]); [hLeft,hRight,hDiv2] = uisplitpane(hDown, 'dividercolor','r', 'dividerwidth',3); t=0:.1:10; hax1=axes('Parent',hUp); plot(t,sin(t)); hax2=axes('parent',hLeft); plot(t,cos(t)); hax3=axes('parent',hRight); plot(t,tan(t)); hDiv1.DividerLocation = 0.75; % one way to modify divider properties... set(hDiv2,'DividerColor','red'); % ...and this is another way...
Making UISplitPane work in HG2 (R2014b onward) was quite a pain: numerous changes had to be made. For example, dynamic UDD properties can no longer be added to Matlab handles, only to Java ones. For Matlab handles, we now need to use the addprop function. For such properties, the UDD meta-property
SetFunction is now called
SetMethod (and similarly for Get) and only accepts function handles (not function handle cells as in UDD). Also, the UDD meta-property
AccessFlags.PublicSet='off' needed to change to
SetAccess='private'. Also, handle.listener no longer works; instead, we need to use the addlistener function. There are quite a few other similar tweaks, but UISplitPanenow hopefully works well on both old (HG1, R2014a and earlier) and new (HG2, R2014b+) Matlab releases. Let me know if you still see unhandled issues.
UIExtras (officially named “GUI Layout Toolbox”) is a toolbox of very useful GUI handling functions related to layout management. Written within MathWorks and originally posted in 2010, it has been under continuous maintenance ever since. While being called a “toolbox”, it is in fact freely-downloadable from the Matlab File Exchange.
The new HG2 introduced in R2014b did not just make code porting difficult for me – uiextras’ developers (MathWorkers Ben Tordoff and David Sampson) also encountered great difficulties in porting the code and making sure that it is backward compatible with HG1. In the end they gave up and we now have two distinct versions of the toolbox: the original version for HG1 (R2014a and earlier) and a new version for HG2 (R2014b+).
In my opinion, uiextras is one of the greatest examples of Matlab code on the File Exchange. It is well-written, well-documented and highly performant (although I would also have preferred it to be a bit more robust, it sometimes complains when used). Readers could benefit greatly by studying its techniques, and today’s subject topic is one such example. Specifically, the split-pane divider in uiextras (used by HBoxFlex and VBoxFlex) is simply a Matlab uicontrol having a custom CData property. This CData value is computed and set programmatically (at the bottom of uix.Divider) to display a flat color with some markings at the center (“hand-holds”, a visual cue for interactive dragging, which can be turned off if requested). Thus, for a vertical divider (for an HBoxFlex container) of height 100 pixels and width of 5 pixels, we would get a CData of 100x5x3 (x3 for RGB colors):
hHBox = uiextras.HBoxFlex('Spacing',6, 'BackgroundColor','b'); % Spacing=6 means divider width =6px, and CData width =5px hLeft = uicontrol('parent',hHBox, 'style','check', 'string','Left split pane'); hRight = uicontrol('parent',hHBox, 'style','radio', 'string','Right split pane');
The divider handle is a private property, so we cannot readily access it. However, as I have explained last year, we can use the builtin struct function:
oldWarn = warning('off','MATLAB:structOnObject'); % temporarily disable warning message on discouraged usage hHBox_data = struct(hHBox); % this includes hidden/private properties warning(oldWarn); hDividers = hHBox_data.Dividers; % multiple dividers are possible in HBoxFlex/VBoxFlex cdata = get(hDividers(1), 'CData');
A very nice trick here is that this divider uicontrol is not a pushbutton as we might have expected. Instead, it is a checkbox control. And while it does not look anything like a standard checkbox (due to the custom CData), checkboxes (and radio-buttons) have a very important advantage that caused them to be preferable over buttons: in buttons, there is always a small border showing at the control’s edges, but checkboxes do not have any 3D appearance, or in other words they do not have a border — their CData can span the entire extent of the control. Neat, right?
This enables us to customize the appearance of checkboxes (and radios) to any arbitrary shape, by setting the relevant CData pixels to transparent/bgcolor (as Robert showed last week for buttons). Matlab GUI controls no longer need to look a boring rectangle. We can have clickable stars, draggable icons, and other similar uses. This really opens up the possibilities for rich GUI appearance. If anyone uses this feature, please do post a comment below (preferably with a nice screenshot!).
Thanks for the comments and I’m glad that you benefited from studying the GUI Layout Toolbox code.
Yes GLT was a rewrite for our code but we worked hard to make sure it wasn’t a rewrite for your code. Package
uiextrasin GLT2 is a compatibility layer to provide support for code that you wrote prior to 14b. User feedback and testing across MathWorks Consulting’s portfolio of customer applications built on GLT suggests that migration effort is none or minor. Nevertheless, if you have problems or suggestions, please get in touch via File Exchange comments.
We wanted to make GLT2 containers real graphics objects (inheritance rather than composition) to enable highly readable code like
child.Parent = gltContainer. However such inheritance wasn’t feasible in HG1, hence the separate downloads for HG1 and HG2.
Finally we revisited the GLT APIs for HG2 and wanted to make some minor changes to property names to improve consistency and ease-of-learning. These are confined to a new package
uix, and we recommend that you use this for new code that doesn’t have to run prior to 14b. Don’t bother migrating pre-14b code to
uix, and you need to use
uiextrasif you want your code to run both pre- and post-14b.