Fanning Software Consulting

Modal and Blocking Widgets

QUESTION: Can you explain the difference between a modal and a blocking widget in IDL? And did widget behavior change? I used to set the Modal keyword on the XManager command, but that no longer works. What's going on?

ANSWER: Rather than re-hash this answer here, I'm just going to let you read a series of articles on this topic from the IDL newsgroup. Here is the first article that I wrote in response to a question by Imanol Echave (ccaeccai@sc.ehu.es):

   Re: Modal base without group leader.  
   Author: David Fanning 
   Date: 1998/06/15
   Forum: comp.lang.idl-pvwave 

   Imanol Echave (ccaeccai@sc.ehu.es) writes:

   > I want to create a modal top-level base without a group leader. 
   > I think that is possible because other IDL widget routines do it. 
   > DIALOG_MESSAGE is an example, this widget is modal and doesn't 
   > need to specify his group leader. How can I do it?

   Presumably if you are writing C code and linking it to IDL with
   LINKIMAGE you can write anything you like, including making calls
   to system-level routines like DIALOG_MESSAGE does. But if you
   are like the rest of us you will have to live with certain
   limitations. This is one of them.

   IDL 5.0 changed the way modal widgets were created, presumably
   to bring the code into compliance with accepted standards that
   are available across all platforms. An admirable goal in theory,
   but a bit of a pain in the neck in practice. In particular, to
   make a modal widget you used to set the MODAL keyword on 
   XManager, but in IDL 5.0 you had to set it on the top-level
   base. BUT, to do that correctly you also had to give the
   top-level base a valid group leader.

   This caused some panic for me because I had quite a few
   useful routines for doing things like selecting and reading
   files that I needed to make modal, but I also wanted to
   call these routines both from within widget programs *and*
   from the IDL command line, where a group leader wasn't
   available. Was it possible to write a program that stopped
   and waited for user input when called both from within a 
   widget program and from the IDL command line?

   Yes. But first I had to learn the fine distinction between
   a "blocking" widget and a "modal" widget. Those of you who
   wrote widget programs in IDL 4 are familiar with blocking
   widgets. These widget programs, when run, "block" the
   IDL command line (similar to any other IDL program). The
   program must be destroyed before the command line comes
   back. This is similar, but not the same, as a modal widget,
   which must also be destroyed before the user can interact
   with other widget programs.

   To create a blocking widget today, you just call 
   XManager without using the No_Block keyword. BUT--and
   this is the most important point--it is only the first
   widget program registered with XManager as a blocking
   widget that blocks the command line. If that blocking
   widget program called other blocking widget programs,
   those other blocking widget programs would NOT block!

   Another way to say this is that those other widget
   programs would have to be written as "modal" widgets
   if you expected them to stop and wait for user input.
   But, again, this is not a problem. Since these other
   programs are being called from within the blocking
   widget program, there is a valid group leader available:
   the top-level base of the blocking program.

   So, back to my problem. How should these blocking/modal
   widget programs be written? Take, for example, a program
   named GetImage that is available on my web page. This
   is a program for requesting information from the user
   about the name, size, data type, etc. of a data file
   that the program opens and reads, returning the image
   data to the caller of the program:

       image = GetImage()

   GetImage is written as a blocking widget. That is to
   say, the XManager call looks like this:

      XManager, 'getimage', tlb, Event_Handler='GETIMAGE_EVENT'

   So if I call it from the IDL command (the command line
   must be unblocked if I can call it) like I do above, then
   it "blocks" and waits for me to fill out the proper fields
   and hit the "Accept" button, which causes the program to
   open the appropriate file and read and return the image data.

   However, if I call GetImage from within a widget program
   (I often do), then I must use a Parent (equivalent to a
   Group_Leader) keyword. (I'm not too excited about this,
   because it makes the Parent keyword a *required* parameter
   in this case, which sort of goes against the point of 
   keywords. I could make it a positional parameter, of
   course, but this is how I chose to implement it.)

      image = GetImage(Parent=event.top)

   How is this implemented? Like this:

      IF N_Elements(parent) EQ 0 THEN $
         tlb = Widget_Base(Column=1, Title='Read Image Data') ELSE $
         tlb = Widget_Base(Column=1, Title='Read Image Data', $
            Modal=1, Group_Leader=parent)

   Now, notice that if I forget to use the Parent keyword and
   I call GetImage from within a *non-blocking* widget program,
   I have no problems at all. When the XManager call in GetImage
   is executed, the program blocks. I only have problems if
   I forget to use the Parent keyword and I am calling GetImage
   from a *blocking* widget program. (Which I hardly ever write,
   thank goodness!) In that case, without the Modal keyword
   being set on the top-level base, GetImage would not block
   and wait for user input. Thus, it would fail to execute 
   correctly.

   How can I know internally if GetImage is being called from
   a blocking or non-blocking widget program? I don't know.
   There may be a way, but I haven't discovered it. I've chosen
   to document the requirements for the program and live with
   a bit of uncertainty.

   Sorry for the long and windy explanation. This is a widely
   misunderstood topic and one that can't be explained easily
   in a few sentences. There is a more complete explanation
   of this topic in my book.

After I wrote this article, I learned from knowledgeable sources at Research Systems that I could modify the XManager code to restore the old modal functionality. Bill Thompson also discoved this, and posted this article on the newsgroup.

   Subject: Using MODAL in IDL/v5.1 and above
   From: thompson@orpheus.nascom.nasa.gov (William Thompson)

   There's been some discussion lately about the changes to the MODAL keyword
   between IDL/v4.0 and later versions of IDL.  In particular, some widget
   programs developed under IDL/v4.0 no longer worked in IDL/v5.1 and above.  RSI
   has given me a fix to xmanager.pro to correct this problem.  Specifically, I
   was told to comment out the line

      WIDGET_CONTROL, ID, /MODAL

   within the routine XMANAGER_EVLOOP_FAKE_MODAL in xmanager.pro.  Commenting this
   line out does appear to make our legacy software work in IDL/v5.1.1.  I'm
   told by RSI that the same fix will also work in IDL/v5.1 and IDL/v5.2.

   The other issue that has been discussed is the additional restrictions that
   have been placed on the new way of using the MODAL keyword within WIDGET_BASE,
   as opposed to the old way of putting MODAL in the XMANAGER call.  The
   particular restriction that bit us was that, using the new syntax, modal
   widgets cannot call nonmodal subwidgets.  This didn't use to be the case.  I
   understand from RSI that they're still considering this issue.  In the
   meantime, they've suggested a way to get around these restrictions.  The
   procedure is as follows:

   1.  The MODAL keyword is not passed to either XMANAGER or WIDGET_BASE.
       Instead, the actions of MODAL are emulated.

   2.  The "modal" widget desensitizes the calling widget.

   3.  Output from the "modal" widget is passed to the calling widget by setting a
       widget value or user-value within the calling widget, rather than using
       parameters in the procedure call.  Part of this output is a parameter which
       is initially set to zero.  When the "modal" widget exits, it sets this
       parameter to one.  This is how the calling routine knows when the "modal"
       widget is done.

   4.  The calling routine activates a timer widget to check if the modal widget
       is still running.  If it's done, then the calling widget resensitizes
       itself, and continues processing.

   This procedure is definitely more complicated than the old style, and requires
   that the "modal" widget knows something about the internal workings of the
   routine that called it.  However, it does appear to work pretty well.

   I'm not sure yet whether we'll try to modify our software along the above
   lines, or continue to depend on the old style of passing MODAL to XMANAGER.
   It'll probably depend on whether we would need to make the changes in 2 or 3
   places, or in hundreds of places.  Each procedure would need to be reviewed to
   see if we can live with simply moving MODAL from XMANAGER to WIDGET_BASE, or
   would need to do something like the above.

   William Thompson

After Bill published his article, Craig Markwardt (craigmnet@astrog.physics.wisc.edu) offered this suggestion for how users could write their programs in this reply to a Brad Gom question on a related issue.

   Subject: Re: xwindow and ps_form questions
   From: Craig Markwardt (craigmnet@astrog.physics.wisc.edu)
   Date: 19 Jan 1999 20:00:49 -0600


   Brad Gom  writes:
   > 
   > I've been trying to add a xwindow widget to my widget program. I have
   > two problems:
   > 
   > 1)  when I use xwindow within a widget program (ie I call xwindow to run
   > a plot routine when a certain widget event occurs) the plot works fine. I can
   > resize properly. (xwindow works perfectly when I call it from a non-widget program)
   > But, when I try to configure a postscript file for output, I get the
   > following error:
   > 
   > > % XMANAGER: Caught unexpected error from client application. Message follows...
   > > % Variable is undefined: CANCEL.
   > >
   > It seems that the main xwindow routine is not waiting for the
   > ps_form routine to return its structure.  The error occurs on this line in xwindow:
   > ...

   Yes, Brad should have mentioned that he was running my version of the
   program.  Up until now, I haven't put any strenuous demands on XWINDOW
   in IDL 5, since we still are mostly an IDL 4 group.  (You may say
   "ugghh", but then again, all of these wierd modal-problems in IDL 5
   seem pretty ridiculous. ... and who needs objects anyway?  chuckle)

   David -- and other interested parties -- I have updated PS_FORM and
   XWINDOW to conform to David's posting on the subject from last June.
   I have ran them through IDL versions 4 to 5.1 and they work now.

    http://astrog.physics.wisc.edu/~craigm/idl/idl.html

   [Donning Scrooge jacket ... ] A pox upon RSI for changing widgets like
   this!

   Here seems to be an elegant and no nonsense way to get your modal
   widgets back without too much hassle:

    1. Read David's posting on modal and blocking widgets from June 15,
       1998.  It's on DejaNews.
    2. If your widget program will be called by *another* widget
       program, then make sure the group leader can be passed in
       via a parent keyword (a la David's suggestion).
    3. Construct your top level base with this snippet

     IF double(!version.release) GE 5 AND n_elements(parent) GT 0 THEN $
       widget_modal = {Modal:1, Group_Leader:parent(0)}
     tlb = Widget_Base(Column=1, whatever_you_want, _EXTRA=widget_modal)

    4. Invoke XMANAGER with this snippet:

     IF double(!version.release) LT 5 THEN xmanager_modal = {Modal:1}
     XManager, 'myfunc', tlb, _extra=xmanager_modal

   How does it work?  The main time there are problems is when you want
   one widget program to call another widget program.  As long as the
   'parent' is passed into your program, the first snippet makes sure
   that WIDGET_BASE is called with the right parameters.  The second
   snippet calls XMANAGER with the MODAL keyword if needed.  I use the
   tricky _EXTRA parameter to avoid maintaining two lines of code which
   do essentially the same thing.

   One added benefit is that widgets created like this work properly on
   both IDL 4 and IDL 5, a win for me.  

   Craig

Google
 
Web Coyote's Guide to IDL Programming