For the application that I will be presenting at next week’s MATLAB Expo in Munich (presentation slides), I wanted to add a text label at a specific location within the figure. The problem was, as you can clearly see from the screenshot below, that there is precious little available space for a new label. I could drive the entire content down to make space for it, but that would reduce the usable space for the actual contents, which is already at a premium:
Adding a transparent label to Matlab GUI (click for full-size image)
A natural place for the new label, as indicated, would be on top of the empty space next to the content’s sub-tabs (Correlation and Backtesting). This empty space is taken up by Matlab’s uitabgroup control, and we can simply place our label on top of it.
Well, easier said than done…
The obvious first attempt is to set the label’s position to
[0,0,1,1](in normalized units of its parent container). The label text will appear at the expected location, since Matlab labels are always top-aligned. However, the label’s opaque background will hide anything underneath (which is basically the entire content).
If we set the label’s position to something smaller (say,
[.2,.9,.6,.1]), the label will now hide a much smaller portion of the content, but will still mask part of it (depending of the exact size of the figure) and for very small figure might actually make the label too small to display. Making the label background transparent will solve this dilemma.
Unfortunately, all Matlab controls are made opaque by default. Until recently there was not much that could be done about this, since all Matlab controls used heavyweight
java.awt.Panel-derived containers that cannot be made transparent (details). Fortunately, in HG2 (R2014b onward) containers are now lightweight
javax.swing.JPanel-derived and we can transform them and their contained control from opaque to non-opaque (i.e., having a transparent background).
There are 3 simple steps for this:
- Find the text label control’s underlying Java peer (control) reference handle. This can be done using my findjobj utility, or by direct access via the containing uipanel hierarchy (if the label is inside such a uipanel), as explained here.
- Set the Java label reference to be non-opaque (via its setOpaque() method)
- Repaint the label via its repaint() method
% Create the Matlab text label uicontrol hLabel = uicontrol('Style','text', 'Parent',hPanel, 'Units','norm', 'Pos',[0,0,1,1], 'String','Results for BERY / PKG (1 hour)'); % Get the underlying Java peer (control) reference jLabel = findjobj(hLabel); %jLabel = hPanel.JavaFrame.getGUIDEView.getComponent(0).getComponent(0).getComponent(0).getComponent(0); % a direct alternative % Set the control to be non-opaque and repaint it jLabel.setOpaque(false); jLabel.repaint();
This now looks nice, but not quite: Matlab displays the label text at the very top of its container, and this is not really in-line with the uitab labels. We need to add a small vertical padding at the top. One way to do this would be to set the label’s position to
[0,0,1,.99] rather than
[0,0,1,1]. Unfortunately, this results in varying amounts of padding depending on the container/figure height. A better alternative here would be to set the label to have a fixed-size padding amount. This can be done by attaching an empty
Border to our
% Attach a 6-pixel top padding jBorder = javax.swing.BorderFactory.createEmptyBorder(6,0,0,0); % top, left, bottom, right jLabel.setBorder(jBorder);
Another limitation is that while the transparent background presents the illusion of emptiness, trying to interact with any of the contents beneath it using mouse clicks fails because the mouse clicks are trapped by the Label background, transparent though it may be. We could reduce the label’s size so that it occludes a smaller portion of the content. Alternatively, we can remove the label’s mouse listeners so that any mouse events are passed-through to the controls underneath (i.e., not consumed by the label control, or actually it’s internal Java container):
jLabelParent = jLabel.getParent; % Remove the mouse listeners from the control's internal container jListener = jLabelParent.getMouseListeners; jLabelParent.removeMouseListener(jListener(1)); jListener = jLabelParent.getMouseMotionListeners; jLabelParent.removeMouseMotionListener(jListener(1));
Using the label’s Java peer reference, we could do a lot of other neat stuff. A simple example for this is the VerticalAlignment or LineWrap properties – for some reason that eludes me, Matlab’s uicontrol only allows specifying the horizontal alignment and forces a line-wrap, despite the fact that these features are readily available in the underlying Java peer.
Finally, while it is not generally a good design practice to change fonts throughout the GUI, it sometimes makes sense to use different font colors, sizes,
faces and/or attributes for parts of the label text, in various situations. For example, to emphasize certain things, as I’ve done in my title label. Such customizations can easily be done using HTML strings with most Matlab uicontrols, but unfortunately not for labels, even today in R2016a. MathWorks created custom code that removes the HTML support in Matlab labels, for reasons that elude me yet again, especially since Matlab upcoming future GUI will probably be web-based so it will also natively support HTML, so maybe there’s still hope that HTML will be supported in Matlab labels in a future release.
Anyway, the bottom line is that if we need our label to have HTML support today, we can use a standard Java
JLabel and add it to the GUI using the javacomponent function. Here’s a simple usage example:
% Create the label and add it to the GUI jLabel = javaObjectEDT(javax.swing.JLabel('<html>Results for <b>BERY / PKG (1 Hour)</b></html>')); [hjLabel, hContainer] = javacomponent(jLabel, [10,10,10,10], hPanel); set(hContainer, 'Units','norm', 'Pos',[0,0,1,1]) % Make the label (and its internal container) transparent jLabel.getParent.getParent.setOpaque(false) % label's internal container jLabel.setOpaque(false) % the label control itself % Align the label jLabel.setVerticalAlignment(jLabel.TOP); jLabel.setHorizontalAlignment(jLabel.CENTER); % Add 6-pixel top border padding and repaint the label jLabel.setBorder(javax.swing.BorderFactory.createEmptyBorder(6,0,0,0)); jLabel.repaint; % Now do the rest - mouse-listeners removal etc. ...
If you happen to attend the Matlab Expo next week in Munich Germany, please do come by and say hello!