The concepts of events and event processing underlie every aspect of widget programming. It is important to understand how IDL handles widget events in order to use widgets effectively.
This section discusses the following topics:
For a discussion of techniques you can use to detect and respond to specific types of events, see Working with Widget Events.
What are Widget Events?
A widget event is a message returned from the window system when a user manipulates a widget. In response to an event, a widget program usually performs some action (e.g., opens a file, updates a plot).
Structure of Widget Events
As events arrive from the window system, IDL saves them in a queue for the target widget. The WIDGET_EVENT function delivers these events to the IDL program as IDL structures. Every widget event structure has the same first three fields: these are long integers named ID, TOP, and HANDLER:
- ID is the widget ID of the widget that generated the event.
- TOP is the widget ID of the top-level base containing ID.
- HANDLER is the widget ID of the widget associated with the event handling routine. The importance of HANDLER will become apparent when we discuss event routines and compound widgets, below.
Event structures for different widgets may contain other fields as well. The exact form of the event structure for any given widget is described in the documentation for that widget’s creation function.
Managing Widget Events with XMANAGER
The XMANAGER procedure provides a convenient, simplified interface IDL’s event-handling capabilities. At the highest level, creating a widget application consists of the following steps:
- Creating routines to react to widget events.
- Creating the widgets that make up the application’s interface.
- Realizing the widgets.
- Calling XMANAGER to manage events flowing from the widget interface.
XMANAGER arranges for an event-handling procedure supplied by the application to be called when events for it arrive. The application is shielded from the details of calling the underlying WIDGET_EVENT function and interacting with other widget applications that may be running simultaneously.
Note: While it is possible for a user-written program to call the WIDGET_EVENT function directly, in practice this is very unusual. For details on how events are handled at a low level, see The WIDGET_EVENT Function.
The file xmng_tmpl.pro, found in the lib subdirectory of the IDL distribution, is a template for writing widget applications that use XMANAGER.
XMANAGER and Blocking
Blocking is when processing by IDL is suspended until some event or action takes place. Unless you specifically arrange otherwise, IDL will only allow one user interface to be active at one time (the IDL command line or a single widget application). XMANAGER simplifies the process of arranging things so that multiple user interfaces can run at the same time; it manages events so that applications do not need to block in order to be assured of receiving the correct event information.
IDL’s blocking behavior is discussed in detail in XMANAGER. In most cases, specifying the NO_BLOCK keyword when calling XMANAGER will allow your application to “play nicely with others,” but you should keep the following things in mind when writing widget applications:
Active Command Line
IDL can provide an active command line. If the command line is active, IDL will execute commands entered at the command line even if one or more widget applications are already running. In order for IDL to behave in this way, all widget applications must be run via XMANAGER with the NO_BLOCK keyword set. See Active Command Line under XMANAGER for details
Blocking and Non-Blocking Applications
By default, widget applications, even those managed with XMANAGER, will block. To enable your application to run without blocking other widget applications or the IDL command line, you must explicitly set the NO_BLOCK keyword to XMANAGER when registering the application. Put another way, any running widget application that does not have this keyword set will block all event processing for widget applications and the IDL command line. See Blocking vs. Non-blocking Applications under XMANAGER for details.
Registering Applications Without Processing Their Events
In order to allow multiple widget applications to run simultaneously, each application must be registered with XMANAGER, so it knows how to recognize events generated by the application. In most cases, the registration step takes place automatically when XMANAGER is called to begin processing events for the application.
In some cases, however, it may be useful to register an application with XMANAGER before asking it to begin processing the application’s events. In these cases, you can use the JUST_REG keyword to XMANAGER; the application is added to XMANAGER’s list of known applications without starting event processing, and XMANAGER returns immediately. See JUST_REG vs. NO_BLOCK under XMANAGER for details.
Tips on Working With XMANAGER
Because XMANAGER buffers you from direct handling of widget events, you cannot explicitly specify an event-handling function or procedure for the top-level base using the EVENT_FUNC or EVENT_PRO keywords to WIDGET_BASE or WIDGET_CONTROL. Event handlers for top-level bases specified via these keywords will be overwritten by XMANAGER.
Instead, provide the name of the event handler routine to XMANAGER via the EVENT_HANDLER keyword. If you do not supply the name of an event handler via the EVENT_HANDLER keyword, XMANAGER will construct a default name by adding the suffix “_event” to the Name argument.
Note that this guideline applies only to top-level bases (base widgets created with no parent widget). Child base widgets should use the EVENT_FUNC or EVENT_PRO keywords to specify event handling routines, if necessary.
In addition, it is often convenient to specify the death-notification routine for the top-level base of a widget application via the CLEANUP routine to XMANAGER rather than via the KILL_NOTIFY keyword to WIDGET_BASE or WIDGET_CONTROL. Either method will work, but the last routine specified is the routine that will be called when the base widget is destroyed. Since the call to XMANAGER is often the last call made when creating a widget application, using the CLEANUP keyword to specify the routine to be called when the application ends is preferred.
The XREGISTERED Function
The XMANAGER procedure allows multiple instances of a widget application to run simultaneously. In some cases, however, you may wish to ensure that only a single instance of application can run at a given time. An obvious example of this is an application that uses a COMMON block to maintain its current state (see Managing Application State).
The XREGISTERED function can be used in such applications to ensure that only a single copy of the application run at a time. Place the following statement at the start of the widget creation routine:
IF (XREGISTERED('routine_name') NE 0) THEN RETURN
where routine_name is the name of the widget application.
See XREGISTERED for further information.
The WIDGET_EVENT Function
All widget event processing in IDL is eventually handled by the WIDGET_EVENT function. Note that while we will discuss WIDGET_EVENT here for completeness, in most cases you will not want to call WIDGET_EVENT directly. The XMANAGER routine provides a convenient, simplified interface to WIDGET_EVENT and allows IDL to take over the task of managing multiple widget applications.
In its simplest form, the WIDGET_EVENT function is called with a widget ID (usually, the ID of a base widget) as its argument. WIDGET_EVENT checks the queue of undelivered events for that widget or any of its children. If an event is present, it is immediately dequeued and returned. If no event is available, WIDGET_EVENT blocks all other processing by IDL until an event arrives, and then returns it. Typically, the request is made for a top-level base, so WIDGET_EVENT returns events for any widget in the widget hierarchy rooted at that base widget.
This simple usage suffers from a major weakness. Since each call to WIDGET_EVENT is looking for events from a specified widget hierarchy, it is not possible to receive events for more than one widget hierarchy at a time. It is important to be able to run multiple widget applications (each with a separate top-level base) simultaneously. An example would be an image processing application, a color table manipulation tool, and an on-line help reader all running together.
One solution to this problem is to call WIDGET_EVENT with an array of widget identifiers instead of a single ID. In this case, WIDGET_EVENT returns events for any widget hierarchy in the list. This solution is effective, but it still requires that you maintain a complete list of all interesting top-level base identifiers, which implies that all cooperating applications need to know about each other.
The most powerful way to use WIDGET_EVENT is to call it without any arguments at all. Called this way, it will return events for any currently-realized widgets that have expressed an interest in being managed. (You specify that a widget wants to be managed by setting the MANAGED keyword to the WIDGET_CONTROL procedure.) This form of WIDGET_EVENT is especially useful when used in conjunction with widget event callback routines, discussed in Event Processing and Callbacks.
Event Processing and Callbacks
Previously, we mentioned that when IDL receives an event, the event is queued until a call to WIDGET_EVENT is made (either explicitly by the user program or by XMANAGER), whereupon the event is dequeued and returned. The following is a more complete description of what actually happens in IDL’s event loop.
Events for a given widget are processed in the order that they are generated. The event processing performed by WIDGET_EVENT consists of the following steps, applied iteratively:
- Wait for an event from one of the specified widgets to arrive.
- Starting with the widget that generated the event, search up the widget hierarchy for a widget with an associated event-handling procedure or function.
- Event-handling routines associated with widgets are known as callback routines. Other cases where an IDL system routine (WIDGET_EVENT, in this instance) calls a user-specified, user-written routine include routines specified via the KILL_NOTIFY or NOTIFY_REALIZE keywords to the widget creation functions and WIDGET_CONTROL, as well as the corollary keywords to XMANAGER.
- If an event-handling procedure is found, it is called with the event as its argument. The HANDLER field of the event is set to the widget ID of the widget associated with the handling procedure. When the procedure returns, WIDGET_EVENT returns to the first step above and starts searching for events. Hence, event-handling procedures are said to “swallow” events.
-
If an event-handling function is found, it is called with the event as its argument. The HANDLER field of the event is set to the widget ID of the widget associated with the handling function.
When the function returns, its value is examined. If the value is not a structure, it is discarded and WIDGET_EVENT returns to the first step. This behavior allows event-handling functions to selectively act like event-handling procedures and “swallow” events.
If the returned value is a structure, it is checked to ensure that it has the standard first three fields: ID, TOP, and HANDLER. If any of these fields is missing, IDL issues an error. Otherwise, the returned value replaces the event found in the first step and WIDGET_EVENT continues moving up the widget hierarchy looking for another event handler routine, as described in step 2, above.
In situations where an event structure is returned, event functions are said to “rewrite” events. This ability to rewrite events is the basis of compound widgets, which combine several widgets to give the appearance of a single, more complicated widget. Compound widgets are an important widget programming concept. For more information, see Creating a Compound Widget.
- If an event reaches the top of a widget hierarchy without being swallowed by an event handler, it is returned as the value of WIDGET_EVENT.
- If WIDGET_EVENT was called without an argument, and there are no widgets left on the screen that are being managed (as specified via the MANAGED keyword to the WIDGET_CONTROL procedure) and could generate events, WIDGET_EVENT ends the search and returns an empty event (a standard widget event structure with the top three fields set to zero).