Some time ago, a reader on the CSSM newsgroup asked whether it is possible to disable an entire figure window while some processing is taking place. Last week, Mike posted an article about the built-in functions uiwait and uiresume on MathWork’s Matlab Desktop blog, which more-or-less answers this need. Today I will expand the answer based on several undocumented/unsupported mechanisms.
Uiwait and uiresume
We start with a short review of the supported mechanism: We can use the built-in uiwait function to suspend Matlab processing until either the specified handle (typically a figure handle) is deleted/closed, a user callback programmatically calls uiresume, or Ctrl-C is pressed. A short example, adapted from uiwait‘s help section (the official help section has a bug, so if anyone reads this at TMW, please fix):
h = uicontrol('Position', [20 20 200 40], 'String', 'Continue', 'Callback', 'uiresume(gcbf)'); f = gcf; disp('This will print immediately'); uiwait(f); disp('This will print after you click Continue or close the window'); % Close the figure (use try-catch in case f is already closed) try close(f); catch, end
Unfortunately, the side effect is that uiwait blocks the regular (non-callback’ed) processing until the lock is released. This is great for simple cases where we require a user to respond to a popup window (e.g., message-box or input dialog) before being allowed to proceed. In more complex situations we may wish to suspend interaction on certain figure windows while continuing to process Matlab functionality – this is impossible to do using uiwait.
Matlab does not have any documented way to disable an entire figure window. You can set the figure’s WindowStyle property to ‘modal’ but this is not the same: (1) it removes the toolbar and menu-bar (2) it still enables interaction with all the figure’s GUI controls and (3) it prevents access to any other Matlab window or desktop until the modal figure is closed. This is certainly NOT what we need.
Of course, we could always loop over all the relevant GUI handles and set their Enable property to ‘inactive’ or ‘off’. But this is tedious, lengthy, and in complex GUIs that have multiple layers of nested panels, it also requires careful recursive programming. We also need to take care to disable hidden handles, toolbars, menu-bars, context-menus, Java components – all these have different ways of being accessed and disabled. And once we uiresume from the lock, we need to remember each handle’s pre-uiwait state, so that if it was disabled before the call to uiwait, it will remain so after uiresume. In short, this is not a trivial exercise at all.
Uisuspend and uirestore
To complicate things, the fully-documented and supported uiwait and uiresume functions have the semi-documented unsupported built-in counterparts uisuspend and uirestore, which are very similar in functionality and name. It is important not to confuse between these two function sets, and most importantly, not to confuse between uiresume and uirestore after using their counterparts earlier in the code – odd things might happen if you do…
Hopefully, in a nearby Matlab release these inconsistencies between the two function sets will disappear and they will be merged, because the current situation is prone to errors and confusion. For backward compatibility considerations, I hope that uisuspend and uirestore would not be discontinued but rather be modified to simply call uiwait and uiresume.
Note that in Matlab releases up to R2010b, uirestore had a minor bug in that it overwrites the figure’s WindowScrollWheelFcn callback with the WindowKeyPressFcn value. This bug was finally fixed in R2011a.
How to enable/disable a figure window
So going back to the main question, we want to find a way to disable an entire figure window, in one simple stroke, and without blocking the Matlab processing.
As usual, we turn to Java and the solution is trivially simple:
First, get the figure window’s underlying Java reference. Note that we need the top-level container reference, rather than the proxy peer handle returned by the JavaFrame hidden property:
>> jFigPeer = get(handle(gcf),'JavaFrame') jFigPeer = com.mathworks.hg.peer.FigurePeer@1ffbad6 >> jWindow = jFigPeer.fFigureClient.getWindow ans = com.mathworks.hg.peer.FigureFrameProxy$FigureFrame[...]
Now remember that all Java GUI components (on which Matlab GUI is based) contain an Enabled property. So, simply set this property on the jWindow reference to true or false:
jWindow.setEnabled(false); % or true % an equivalent alternative set(handle(jWindow),'Enabled',false) % or true
By the way, if you only need to disable the figure’s content area (i.e., not the toolbar and menu bar), simply use a different reference handle:
jFigPeer.getFigurePanelContainer.setEnabled(false); % or true
- While the window looks the same when it is disabled, in reality none of its GUI elements can be clicked or activated (try it!). If you also want the figure to appear blurred you could try using something like a JXLayer semi-translucent glass pane.
- The Java components’ property is called Enabled, not Enable as in pure-Matlab Handle-Graphics controls. Also, the Java property expects true/false (or 0/1), rather than Matlab’s ‘on’/’off’ (there are some fine details here that are release-specific so I will skip them)
- For docked windows the top-level window is the figure’s group container window or the desktop window, which we do not want to disable. Detecting this is easy (check whether the figure’s WindowStyle is ‘docked’), and in such cases simply disable/enable the figure’s content area as shown above.
Use of the JavaFrame property
Note that all this relies on the undocumented hidden figure property JavaFrame, which issues a standing warning (since Matlab release R2008a) of becoming obsolete in some future Matlab release (HG2?):
>> jFigPeer = get(gcf,'JavaFrame') Warning: figure JavaFrame property will be obsoleted in a future release. For more information see the JavaFrame resource on the MathWorks web site. (Type "warning off MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame" to suppress this warning.) jFigPeer = com.mathworks.hg.peer.FigurePeer@1ffbad6
To remove the above warning I have used (note the handle wrapper, as suggested by Donn Shull):
jFigPeer = get(handle(gcf),'JavaFrame')
If and when JavaFrame does become obsolete in some future Matlab version, be sure to look in this blog for workarounds.
You may also wish to inform MathWorks on the dedicated webpage that they have set up for specifically this reason (http://www.mathworks.com/javaframe), how you are using JavaFrame and why it is important for you. This may help them to decide whether to keep JavaFrame or possibly add the functionality using other means.
The enableDisableFig utility
In 2007, I created the enableDisableFig utility that wraps the above code in an easy-to-use Matlab utility that I have uploaded to the File Exchange.
enableDisableFig returns the current state of the specified figure handle (or gcf if no handle is specified), and optionally sets the figure’s enabled/disabled state to a new value (‘on’,’off’, true or false):
>> oldState = enableDisableFig(gcf, 'on') oldState = on >> enableDisableFig(oldState); >> enableDisableFig(true); % an alternative
enableDisableFig also provides some sanity checks and error handling that should always appear in any real-world application. In this blog I generally tend to provide only bare-bones code snippets (as above). But in any respected application we must check the validity of input arguments and return values, catch run-time errors, compensate for incompatibilities with older Matlab release and so on. Take a look at enableDisableFig‘s source code to see how I have done this.
When any GUI control is disabled, the accepted convention is to shade it in a gray color as a visual aid to the user that the control is currently disabled. This is automatically done by Matlab when any control’s Enable property is set to ‘off’. In our case, it would be very helpful to display a similar blurring effect for the disabled figure window.
Would you be very surprised if I told you that this is impossible to do with regular Matlab, but trivially easy to achieve using a tiny bit of Java magic powder? I’ll expand on this in my next post, but here’s something to keep you waiting:
This weekend (9-10 April, 2011) I plan to upgrade the website. This will entail moving to a different webserver, database, upgraded blogging application, new design etc. This site may be down or “goofy” for quite a few hours. Hopefully all will be back to normal by Monday and I hope you will like the new design.