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 Gomwrites: > > 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
Copyright © 1997-1999 David W. Fanning
Last Updated 1 June 1999