Getting rid of common blocks

Widgets are graphical user interface that you can use to manipulate dataset, launch analysis, and just about everything IDL can do, with series of mouse clicks. I would actually distinguish two kind of widgets:

  1. widgets that do not return variables to the caller, such as display widgets, or widgets that just print out information.
  2. widgets that do return variables to the caller, such image manipulation or data processing widgets
The reason why I make this distinction will, I hope, become clear later.

A widget program is made up of typically two elements: (a) a main routine that receives your input, if any, defines the widget aspect (i.e. sets up the interface), and if it belongs to class (2) hands back data to the caller, and (b) an event handling routine that actually does the job (i.e. reacting to your actions).

There is in fact a third element called xmanager which is an IDL code that triggers the event-handling routine each time you do something on the widget. It is the job of element (a) to start xmanager and to provide it with both the widget identity and its associated event-handling routine. This is the origin of the main widget trick: the main routine (a) does not call the event-handling routine (b) itself and therefore, it cannot pass parameters to it the way you usually pass parameter to a routine or function. Yet it is clear that you want to pass parameters to the event-handling routine since it is the one that is actually doing the job.

Related to that problem is the fact that some of your actions can modify the data and that you want that subsequent actions work on the modified version of the data, not the one you provided. Yet the event-handling routine runs once each time you generate an event, i.e. click on the widget. For instance, imagine you create a display widget that lets you choose, through min and max sliders, the high and low cuts of the display. Inside the event-handling routine of the widget, there would be a line such as:

tvscl,image›the_minimum ‹the_maximum

Where the_minimum and the_maximum are defined by your action on the min and max sliders. Obviously you cannot set both the min and max with the same action so you have to set them sequentially. The event-handling routine will be run by xmanager each time you slide the sliders and thus for anything meaningful to happen, the event-handling routine has to remember the result of previous actions.

Now passing parameters to the event-handling routine or making it remember its previous actions are in fact the same problems. A classical way to solve it is to use commons, i.e. all the variables that the widget manipulates are stored in a common and therefore they are always accessible, either to the main routine or to the calling routine. Through the common you can pass data to the event-handling routine, and it can remember the result of previous actions. However this has a number of drawbacks:

The real solution to that problem comes through a recent addition to widgets: their user value. The user value is, as the name indicates, a value that is set by the user and tied to the widget. This value can be anything, even a structure. Therefore, you can use that user value as a common. The simplest way is to define in the main procedure (element a) a structure that will hold all the variables you need (even widget IDs) and store it in the user value of the base widget (that is the first widget created and the last destroyed, this is also the only widget whose ID is always contained in the event structure that xmanager passes to the event handling routine).

This is done with the command:

widget_control,the_base,set_uvalue=main_str

In this example, the_base is the widget ID of your base widget, main_str is the name of the structure containing all the variables, and widget_control is the IDL command that can alter widget properties.

Then, in the event handling routine, you need to recover this structure. To do that, the only information you need is the ID of the base widget. This is stored in the event structure that your routine receives from xmanager, in the top tag. If you call this structure event then you can retrieve the variable structure with:

widget_control,event.top,get_uvalue=main_str

If you don't forget to update the main structure in the user value of the base at the end of the event handling routine (with a widget_control,event.top,set_uvalue=main_str), then this user value plays the part of the common and the trick is done. An example of such a routine can be viewed here (this is a display widget that I have used to inspect savesets).

Well the trick is done for the type 1 widgets only because we have not solved the problem of making the event handling routine (b) talk back to the main widget routine (a). Indeed the only way to go back to the main routine is by quitting the widget, i.e. destroying the base widget (as well as all its child widgets). Once it is destroyed, the widget ID will not refer to anything anymore, and thus when you are back in the main routine, you have no way of accessing the user value of the base where the variables are stored. There is a solution to that problem, though it is not as elegant as the one presented here for type 1 widgets.

In type 2 widgets, I repeat, the problem is to have the event handling routine sent the resuts of its computation to the main widget routine. Since to go back to the main widget routine, we have to destroy the base widget, we cannot use the same trick (i.e. widget_control,the_base,get_uvalue=main_str after the xmanager call in the main routine produces an error, because the base ID no longer refers to a valid widget.

The solution is to use the user value of an independent widget. If you have coded widgets, you know that widgets belong to a hierarchy, usually starting at a main base (it is that base that I have used for the type 1 widgets). Nothing prevents you from creating more than one widget hierarchy in a single program. You know also that widgets need to be defined (with the widget_base and related commands) but also to be realized, i.e. to actually make them active and appear on your screen. Well, nothing prevents you from creating a widget without realizing it. Once created, a widget has a user value and therefore you can use this user value as a pseudo-common to establish a two-way dialog between the main widget program and its event handling routine. In the following, the ID of this widget will be stored in a variable called the_dialog and I'll refer to it as the dialog widget.

You still have to use the user value of the realized base widget however, because its ID will be the only one transfered to the event handler routine. The elegant solution then is to use the user value of the dialog widget to store all the variables went in the user value of the base in the type 1 widgets case and store the ID of the dialog widget in the user value of the base widget. Thus prior to the xmanager call in the main routine you would find the lines:

widget_control,the_dialog,set_uvalue=main_str

widget_control,the_base,set_uvalue=the_dialog

And in the event handling routine, you would access to the main structure containing the common data with:

widget_control,event.top,get_uvalue=the_dialog

widget_control,the_dialog,get_uvalue=main_str

In the main code, after the call to xmanager (i.e. in the blocking mode, for the part of the code that is executed after the base widget has been destroyed) you can recover the results of the widget with:

widget_control,the_dialog,get_uvalue=main_str

To be clean, don't forget to destroy the dialog widget after you have recovered the variables. An example of this code can be viewed here in a widget that I use to fit surfaces to parts of images.

These two tricks allow to get rid of commons in both types of widgets and thus activate many copies of the same widgets. They also simplify the developing process. I hope you find this useful.