Enable/disable entire figure window

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

Notes:

  • 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.

Visual aid

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:

A blurred disabled figure

A blurred disabled figure

Server upgrade

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.

Related posts:

  1. Minimize/maximize figure window Matlab figure windows can easily be maximized, minimized and restored using a bit of undocumented magic powder...
  2. Transparent Matlab figure window Matlab figure windows can be made fully or partially transparent/translucent or blurred - this article explains how...
  3. Blurred Matlab figure window Matlab figure windows can be blurred using a semi-transparent overlaid window - this article explains how...
  4. Detecting window focus events Matlab does not have any documented method to detect window focus events (gain/loss). This article describes an undocumented way to detect such events....
  5. Customizing figure toolbar background Setting the figure toolbar's background color can easily be done using just a tiny bit of Java magic powder. This article explains how. ...
  6. Figure toolbar customizations Matlab's toolbars can be customized using a combination of undocumented Matlab and Java hacks. This article describes how to customize the Matlab figure toolbar....

Categories: Figure window, GUI, Hidden property, Java, Medium risk of breaking in future versions

Tags: , , , ,

Bookmark and SharePrint Print

26 Responses to Enable/disable entire figure window

  1. Koelemay says:

    Hi Yair, great post!

    “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.”

    I believe you could avoid the complexity described by using findobj:

    h = findobj(figHandle,'Enable','on');

    h contains handles to all objects with “Enable” set to “on” (even through nested panels) and then you can simply:

    set(h,'Enable','off')

    Of course, there is still the issue of remembering the state of the pre-disabled figure so that you only enable those there were beforehand. I’ve done this in the past either with persistent or storing the state in the figure appdata.

    • @Kolemay – thanks for your comment. In general you are correct. However, it’s slightly more complicated:

      * Firstly, we need to use findall rather than findobj, in order to also process hidden handles (such as the toolbar/menubar
      * Secondly, it doesn’t work for axes – you will still be able to interact with axes (zoom, pan, context menu etc.) since Matlab axes don’t have a simple Enable property
      * Thirdly, it doesn’t work for Java components added to the figure using the javacomponent function or its relatives.
      * Finally, it also doesn’t work for ActiveX controls added to the figure using the actxcontrol function.

      Each of these require separate tailored disabling. The Java disabling method I’ve shown automatically takes care of everything.

  2. Ian Andolina says:

    Hi Yair, this is failing for me in 2011b (OS X):

    No appropriate method, property, or field fFigureClient for class com.mathworks.hg.peer.HG1FigurePeer.

    If I replace “fFigureClient” with “fHG1Client” it then works:

    obj.jFrame = get(handle(obj.uihandle),'JavaFrame');
    obj.jWindow = obj.jFrame.fHG1Client.getWindow;
  3. I was using a figure as webfigure (Java JA builder), and I wanted to get rid of the popup window ( the figure window ), while this happens at server GUI.

    This is how to suppress the window being created:

    f = figure('Visible','off');
    // Do stuff here
    webFigureHandle = webfigure(f);
    close(f);

    ps: your WP installation is outdated

  4. Quenten says:

    Hi Yair,

    Thanks for your detailed article. They’re always excellent!

    I have been looking for a way to disable controls and menus while I populate a figure with controls, set up axes, etc so that the user can’t access callbacks before everything is set up.

    Matlab Help says that the OpeningFcn of a GUIDE figure executes just before the figure becomes visible, but in several cases I have the figure appear well before the end of OpeningFcn is complete, which lets the user muck everything up.

    I thought your code would be pretty handy as I’d planned to disable it at the top of OpeningFcn and enable it at the bottom before it returns or enters uiwait. It doesn’t seem to work before the figure is visible. (Fair enough. :) ) I was wondering if you had any idea if I can find out exactly when the figure appears so it can be disabled using the enableDisableFig function.

    • @Quenten – here’s a simple idea that you can try:

      1. at the top of your OpeningFcn, create a javax.swing.JButton and set its ComponentShownCallback to some special Matlab function wasShown
      2. add this component to your figure via the javacomponent function
      3. in your wasShown function, which should get called when the figure becomes visible, you can immediately hide/delete the javacomponent and then disable the entire figure

      I never tested this myself so please report back whether it actually works or not.

  5. Pingback: Matlab

  6. Pingback: Waiting for asynchronous events | Undocumented Matlab

  7. Pingback: HG2 update | Undocumented Matlab

  8. Jed says:

    Do you know if this still works in Matlab 2012a?

    I got the following error:

    >> jFigPeer = get(handle(gcf),'JavaFrame')
    jFigPeer =
      com.mathworks.hg.peer.HG1FigurePeer@628016f7
     
    >> jWindow = jFigPeer.fFigureClient.getWindow
    No appropriate method, property, or field fFigureClient for class com.mathworks.hg.peer.HG1FigurePeer.
    • @Jed – the internal field name has changed over the years. The following code snippet should be backward and forward compatible (for the near future at least):

      jFrame = get(handle(hFig), 'JavaFrame');
      try
          % This works up to R2011a
          jWindow = jFrame.fFigureClient.getWindow;
      catch
          try
              % This works from R2008b and up, up to HG2
              jWindow = jFrame.fHG1Client.getWindow;
          catch
              % This works in HG2
              jWindow = jFrame.fHG2Client.getWindow;
          end
      end
    • Jed says:

      Yair,

      Thanks for the very fast and helpful reply… you have a great site here!

      Your suggestion seemed to work in that I no longer get the error, but it still doesn’t have the desired effect, unfortunately.
      I tried the code below and when I click on the button, I still get the beep and the “hello world” echoed to the screen when I click on the uicontrol. I guess I am doing something wrong, or I misunderstand what to expect.

      >> figure
      >> h = uicontrol;
      >> set(h,'callback','beep,fprintf(''hello world\n'');')            
      >> jFrame = get(handle(gcf),'JavaFrame')            
      jFrame =
      com.mathworks.hg.peer.HG1FigurePeer@f7d11f0
      >> jWindow = jFrame.fHG1Client.getWindow
      jWindow =
      com.mathworks.hg.peer.FigureFrameProxy$FigureFrame[fClientProxyFrame,393,247,576x483,layout=java.awt.BorderLayout,title=Figure 1,resizable,normal,defaultCloseOperation=DO_NOTHING_ON_CLOSE,rootPane=com.mathworks.widgets.desk.DTRootPane[,8,30,560x445,layout=com.mathworks.widgets.desk.DTRootPane$DTRootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=449,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
      >> set(handle(jWindow),'Enabled',false)
      >> get(handle(jWindow),'Enabled')
      ans =
           0
    • Jed says:

      For now, I found I can minimize the window when I want it disabled:

      jFrame.fHG1Client.setMinimized(1)

      This works well enough for my purpose.

    • jWindow.setEnabled(false)
    • Jed says:

      Thanks again, but I still get the same result with

      jWindow.setEnabled(false)

  9. Matthias says:

    I’m currently working on a resize function for a GUI but I don’t want my users to go below a certain resolution.
    The problem I have is that

       jWindow = jFrame.fHG1Client.getWindow;

    always returns an empty array.
    Here’s the entire function:

    function gui_CreateFcn(hObject, eventdata, handles)
       jFrame  = get(hObject, 'JavaFrame');
       jWindow = jFrame.fHG1Client.getWindow;
       jWindow.setMinimumSize(java.awt.Dimension(200,200));

    The other 2 versions provided in this post, namely

       jWindow = jFrame.fFigureClient.getWindow;
       jWindow = jFrame.fHG2Client.getWindow;

    crash since there’s no field named fFigureClient or fHG2Client for class com.mathworks.hg.peer.HG1FigurePeer.

    My GUI was created with guide and consists of a few buttons, an axes and a few text elements but I don’t think that matters to much.
    From what I understand getWindow should return the jWindow even if the gui just consists of a grey screen right?
    Does anyone know why getWindow returns an empty array here?

    • @Matthias – it looks like you’re using a GUIDE-generated GUI. Try placing your JavaFrame code in the figure’s OutputFcn, not its CreateFcn, since the Java peers are not instantiated until the figure becomes visible.

    • Matthias says:

      @Yair – Thank you very much. It worked liked a charm.

  10. Charlie says:

    Yair, I’m using your code in my GUI in Matlab R2013a and it seems it doesn’t work. The code doesn’t give any error, but all the components are still enabled except a popupmenu.

    Get javaframe gives this result:

    com.mathworks.hg.peer.HG1FigurePeer@37953f1e

    Then, jWindow is:

    com.mathworks.hg.peer.FigureFrameProxy$FigureFrame[fClientProxyFrame,296,107,772x504,invalid,layout=java.awt.BorderLayout,title=Módulo de Movimientos,normal,defaultCloseOperation=DO_NOTHING_ON_CLOSE,rootPane=com.mathworks.widgets.desk.DTRootPane[,2,35,768x465,layout=com.mathworks.widgets.desk.DTRootPane$DTRootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=449,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]

    Do you know where could be the problem?

    Best regards,

    Charlie

    • @Charlie – the components may appear enabled, but if you try to click on them (or anywhere in the window) you will see that the entire window is disabled.

  11. John says:

    What about R2013?

    jFrame = get(handle(hFig), 'JavaFrame');
    try
        % This works up to R2011a
        jWindow = jFrame.fFigureClient.getWindow;
    catch
        try
            % This works from R2008b and up, up to HG2
            jWindow = jFrame.fHG1Client.getWindow;
        catch
            % This works in HG2
            jWindow = jFrame.fHG2Client.getWindow;
        end
    end
  12. Moritz says:

    Hi Yair,
    I have a startup GUI, that opens other GUIs by clicking on buttons. If they are opened the first time, the user is able to enter data in uicontrols etc. By closing, I just set visible off and if the user opens it again, it will be set on. So the data remain in the textfields, tables,… If it is reopened, the user should not be able to change the data.

    Therefor I used this code to lock it, after making it visible:

    jFrame = get(handles.esr_basic, 'JavaFrame');
    jWindow = jFrame.fHG1Client.getWindow;
    set(handle(jWindow),'Enabled',false);

    Problem: The GUI appears, is locked and can’t be closed again.

    Afterwards I just tried to lock the content area:

    jFrame = get(handles.esr_basic, 'JavaFrame');
    jFrame.getFigurePanelContainer.setEnabled(false);

    Problem: Nothing happened. Uicontrols can be used like before. Just the warning, that the property will be obsoleted in future release appears, no error.
    In this special case, there are radio buttons in button groups. A simple push button outside the button groups is also still working.
    Did I do something wrong? Do you know a solution?
    Thanks!

    • You’d need to disable the individual controls. Disabling the content pane, root pane, or any other internal panel does not work, AFAIK.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

<pre lang="matlab">
a = magic(3);
sum(a)
</pre>