All Matlab figures have a standard frame around them, consisting of a border and title bar. In some cases it could be useful to present a figure window, displaying only the contents without the border and title bar. Such a borderless (undecorated) window is not possible in Matlab. Well, at least not in supported/documented Matlab…
Readers of this blog and/or my Matlab-Java programming book are aware that Matlab’s GUI is based on Java Swing. In the end, every Matlab figure window is a simple Java JFrame
, and JFrames have a setUndecorated(true) method that can be called to remove the border and title bar.
An undecorated Matlab figure window
Attempt #1 – direct invocation
Unfortunately, we cannot directly call setUndecorated in Matlab, because it only works when the JFrame
has not yet been displayed:
% Get the figure's underlying Java JFrame reference handle >> mjf = get(handle(gcf), 'JavaFrame'); >> jWindow = mjf.fHG2Client.getWindow % or: mjf.getAxisComponent.getTopLevelAncestor jWindow = com.mathworks.hg.peer.FigureFrameProxy$FigureFrame[fClientProxyFrame,740,-761,576x509,...] % Try to remove decoration >> jWindow.setUndecorated(true) Java exception occurred: java.awt.IllegalComponentStateException: The frame is displayable. at java.awt.Frame.setUndecorated(Unknown Source)
On the other hand, if we try to call setUndecorated on a new invisible figure then we’d see that the JFrame
component is not yet created by Matlab:
% Create a simple Matlab figure hFig = figure('Name','Plot example', 'Visible','off'); % Get the underlying Java JFrame reference handle (empty) >> mjf = get(handle(hFig),'JavaFrame'); >> jWindow = mjf.getAxisComponent.getTopLevelAncestor jWindow = [] >> jWindow = mjf.fHG2Client.getWindow jWindow = [] >> jWindow.setUndecorated(true) Attempt to reference field of non-structure array.
So we have a catch-22 situation: we can’t call setUndecorated until the JFrame
is created, but Matlab only creates it when it displays the figure window, and then it’s too late to undecorate…
Attempt #2 – reparenting: an optical illusion
One way that I found around this problem is to reparent the Matlab JFrame
‘s content onto a pure Java JFrame
that has been made undecorated. To hide the Matlab figure after creating the JFrame
, we can simply move the figure’s position to be outside the visible monitor area:
% Create a simple Matlab figure (visible, but outside monitor area) t = 0 : 0.01 : 10; hFig = figure('Name','Plot example', 'ToolBar','none', 'MenuBar','none'); hLine = plot(t, cos(t)); hButton = uicontrol('String','Close', 'Position',[307,0,45,16]); % Ensure that everything is rendered, otherwise the following will fail drawnow; % Get the underlying Java JFrame reference handle mjf = get(handle(hFig), 'JavaFrame'); jWindow = mjf.fHG2Client.getWindow; % or: mjf.getAxisComponent.getTopLevelAncestor % Get the content pane's handle mjc = jWindow.getContentPane; mjr = jWindow.getRootPane; % used for the offset below % Create a new pure-Java undecorated JFrame figTitle = jWindow.getTitle; jFrame = javaObjectEDT(javax.swing.JFrame(figTitle)); jFrame.setUndecorated(true); % Move the JFrame's on-screen location just on top of the original jFrame.setLocation(mjc.getLocationOnScreen); % Set the JFrame's size to the Matlab figure's content size %jFrame.setSize(mjc.getSize); % slightly incorrect by root-pane's offset jFrame.setSize(mjc.getWidth+mjr.getX, mjc.getHeight+mjr.getY); % Reparent (move) the contents from the Matlab JFrame to the new JFrame jFrame.setContentPane(mjc); % Make the new JFrame visible jFrame.setVisible(true); % Hide the Matlab figure by moving it off-screen pos = get(hFig,'Position'); set(hFig, 'Position',[-1000,-1000,pos(3:4)]); drawnow; |
Matlab figures are not pure JFrame
but rather a subclass (com.mathworks.widgets.desk.DTSingleClientFrame
). So instead of creating a new JFrame
, we could create a new instance of Matlab’s class instead, potentially solving a few problems:
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance; jFrame = javaObjectEDT(com.mathworks.widgets.desk.DTSingleClientFrame(jDesktop, figTitle)); % ...the rest is exactly the same as above... |
Either way, we now get a nice undecorated window containing our Matlab contents (see screenshot above).
Once the undecorated JFrame
becomes visible, we should not hide or delete the original Matlab figure, because this will stop rendering of the contents.
Working with the undecorated frame
We can modify the Matlab figure and its contents normally, just as if they still appeared within the original Matlab figure. This can be used to display a dynamic application splash-screen, that displays information on various initialization/loading steps. Or alternately we could use it to display a system health-monitor (small panel with red/green indicators), or maybe a graph with streaming market data or live news alerts. The usages are endless, limited only by your imagination, not by Matlab.
For example, let’s modify our plot dynamically using a timer, and set the close button’s callback to dismiss the new JFrame
and original figure:
% Set a timer to dynamically update the plot every 0.1 sec start(timer('TimerFcn', {@timerCallback,hLine}, 'ExecutionMode','fixedRate', 'Period',0.1)); % Set the close button callback set(hButton, 'Callback',{@closeCallback,jFrame}); % This is the close button's callback function closeCallback(hButton, eventData, jFrame) delete(ancestor(hButton,'figure')); dispose(jFrame); end % This is the plot timer's callback function timerCallback(hTimer, eventData, hLine) xdata = get(hLine,'XData') + 0.1; set(hLine, 'XData',xdata, 'YData',cos(xdata)); xlim([min(xdata) max(xdata)]); end |
Note: if you don’t hide the toolbar/menubar in the original Matlab figure, some of their functions will not work properly in the new JFrame
(e.g., zoom, pan etc.). But in most use-cases we do not want a toolbar/menubar in an undecorated window. The example above showed an undecorated window without the toolbar/menubar.
If we wish to avoid having the new JFrame
appear in the Operating System’s taskbar, we can use the following command:
jFrame.setType(javaMethod('valueOf','java.awt.Window$Type','UTILITY')) |
Note that this command must be executed before the JFrame
is made visible. Also note that it only works with Java 7, in other words Matlab R2013b (8.2) or newer (or if you are very adventurous and happen to use an older Matlab with a custom Java 7 installation).
But now that there is no taskbar component, how can we transfer focus to our new undecorated window? In other words, if another windows hides our new undecorated frame, how can we get it back on top?
A simple solution is to set the Matlab figure frame’s FocusGainedCallback to requestFocus for the newly created jFrame
. Then, when we click the Matlab figure’s button on the taskbar, the new jFrame
will pop into focus. In effect, this provides the optical illusion that the taskbar button refers to the undecorated window (since the actual Matlab figure is positioned beyond the monitor’s area):
hjWindow = handle(jWindow, 'CallbackProperties'); set(hjWindow, 'FocusGainedCallback', @(h,e)jFrame.requestFocus); |
Likewise, we should instrument our Matlab figure so that when it is closed/deleted, so too is our new jFrame
:
set(hjWindow, 'WindowClosedCallback', @(h,e)jFrame_.dispose); |
undecorateFig and redecorateFig
I have encapsulated all of the above in a couple of very easy-to-use utilities that I just posted on the Matlab File Exchange: undecorateFig, redecorateFig. Using them can’t get any simpler than this:
undecorateFig; % undecorate the current figure (gcf) undecorateFig(hFig); % hFig is any GUI handle (needs to be visible) redecorateFig; % redecorate the current figure (gcf) redecorateFig(hFig); % hFig is any GUI handle |
Any sufficiently advanced technology is indistinguishable from magic – Arthur C. Clarke 🙂
Have you made some interesting use of this nice feature in your application? If so, please share it in a comment below.
A suggested related mini project for anyone interested: add an image reflection of the current figure contents beneath the figure. You could use my ScreenCapture utility or directly use java.awt.Robot.createScreenCapture(mjc.getLocationOnScreen)
, process the resulting image (blur, apply transparency gradient, crop at 50% height etc.) and place the resulting image within an undecorated JFrame
placed directly beneath the main figure. You can instrument the figure (hjWindow
)’s ComponentMovedCallback and ComponentResizedCallback to move/resize the reflection JFrame
whenever the parent figure moves or is resized. You could use a timer to periodically update the reflection image so that it remains synchronized with the parent. You’ll probably also want to add a nice toggle-button to the figure’s toolbar to turn the reflection on/off. Maybe I’ll hack this project someday when I have some spare time. Or maybe someone will beat me to it… Care to try?
Hi Yair,
Great post and very useful to know but isn’t this type of figure modification in breach of MathWorks license agreement?
http://www.mathworks.com/matlabcentral/answers/91579-is-it-possible-to-control-the-appearance-of-the-title-bar-and-the-title-bar-icon-of-figure-windows-i
@David – I was (and am) not aware that this violates any license agreement. I am an engineer and not a lawyer. If anyone thinks that it violates the license then they should indeed not use this technique. I do NOT condone any action that violates the license.
I do not see, in my non-expert eyes, how this could violate any Matlab license, considering the fact that we’re not modifying the Matlab figure’s frame/border – we’re simply creating a new Java JFrame, which coexists alongside the original (decorated) Matlab figure. Doing this should be perfectly ok, I think. Note that this is not legal advise in any way – I am NOT a lawyer. Again – if you think that doing this is problematic, then you should not do it.
Hi Yair
The line:
gives me an
Undefined variable "javax" or class "javax.swing.jFrame".
messageBy the way, about the violation of Matlab licence, I think that if the figure still appears in the taskbar with a Matlab icon, then you are not hiding the Matlab belonging of the figure.
But I am a physicist and not a lawyer so don’t use it as a legal advice.
@Ruben – you got the error because you spelled
JFrame
(injavax.swing.JFrame
) with a lowercase j instead of an uppercase JNow I meet a ticklish problem when I use your tool,’undecorateFig’. First I would like to describe my purpose. When we run a GUI we write, a corresponding figure always appear. now I want to full screen the figure, not just maximizing the figure, simultaneously, the most important is that the titlebar of the figure and taskbar should all be hidden or removed. The ‘undecorateFig’ tool can remove titlebar and taskbar of the figure, but the figure is not a full-screen figure, the area corresponding to the previous titlebar isn’t filled by the background of figure. I don’t know how to deal with it. Could you help me? Thanks. For example, I have the following codes:
However, after I use ‘undecorateFig’ tool, the ‘OK’ button doesn’t work .
Best regards!
@Cheung – you can increase the figure’s height by 20 pixels to take into account the titlebar (i.e., screen(4)+20).
As for your OK button, you should review how to specify Matlab callbacks.
@Yair – Thank you for your speedy response. I have tired your suggestion, but it doesn’t work,and remains the previous status, 20 pixels isn’t increased, I don’t why.
Hello Yair,
I’ve tried both the code above and the functions you submitted. I’ve got 2 areas of feedback:
1. The figure sizing isn’t quite correct. It was a bit large and I had to remove the “+mjr.getY” term to get it to fit properly.
2. There are focus traversal problems with the current function. When I use it, I’m unable to input any text or use any of the UI elements inside as the focus keeps transferring elsewhere before any text can be input.
On a side note, I’m using this to generate a popup menu inside of another GUI. It works pretty well, and I don’t think it would be too much work to clean it up. I think it would do well as a MATLAB file submission if labeled as a ‘popup’ menu template.
By the way, I’m using 2014a.
Hello Yair,
I started seeing the following warning when trying to access the JavaFrame property:
I might be mistaken here, but I don’t think I saw this before R2015a. Should we be worried?
I mainly use it to maximize figures, as per your tip from long ago, and did not look into new methods since the introduction of HG2. Is there another way to maximize figures now without getting warnings?
Thanks!
I remembered that this issue was discussed in your blog before. I must’ve hidden the warning in the past…
Hello Yair,
Is it possible to grab and drag a ‘frameless’ figure without the title bar? I would like to click somewhere in the frameless figure area, and drag it around.
Kind regards
@David – you will need to do this programmatically: this is typically handled by the window frame but without the frame it doesn’t happen automatically. So trap a mouse click event and then move the figure as per subsequent mouse-movement events.
Hello Yair,
not sure if you’re still reading comments here but I will give it a shot. I love the functions you provided on the File Exchange. However, when trying to undecorate a GUI created with the App Designer I ran into some problems.
My idea was to include the undecorate.m file in the start up function but it produces the following error:
Cannot undecorate a docked figure.
Note that I am not too familiar with handling MATLAB figures so this might be easy to fix but I just don’t know how.
Regards, Jan
@Jan – undecorate only works with legacy (Java-based) figures, not the new App-designer-created uifigures (which use a completely different technology).