Working with Widget Events

Widget events and the process of establishing a widget event loop for your application are described in Widget Event Processing. This section discusses additional topics that may be useful when creating event-driven applications, including:

Interrupting the Event Loop

Beginning with IDL version 5, IDL has the ability to process commands from the IDL command line while simultaneously processing widget events. This means that the IDL command line will remain active even when widget applications are running.

It is possible to interrupt the event function by sending the interrupt character (Control-C). However, you may find that even after sending the interrupt character, IDL does not immediately interrupt the event loop. IDL will interrupt the process that is “on top”—that is, if several applications are running at once, the interrupt will be handled by the first application to receive it.

If your widget application is the only active application, and sending the interrupt does not cause it to break, move the mouse cursor across (or click on) one of the widgets.

This works because when IDL is in the event function, it only checks for the interrupt between event notifications from the window system. Such events do not necessarily translate one-to-one into IDL widget events because the window system typically generates a large number of events related to the window system’s operation that IDL quietly handles. Moving the mouse cursor across the widgets typically generates some of these events which gives IDL a chance to notice the interrupt and act on it.

Identifying Widget Type from an Event

Given a widget event structure, often you need to know what type of widget generated it without having to match the widget ID in the event structure to all the current widgets. This information is available by specifying the STRUCTURE_NAME keyword to the TAG_NAMES function:

PRINT, 'Event structure type: ', TAG_NAMES(EVENT, /STRUCTURE_NAME)

This works because each widget type generates a different event structure. The event structure generated by a given widget type is documented in the description of the WIDGET_BASE function.

When using this technique: although all the basic widgets use named structures for their events, many compound widgets return anonymous structures. This technique does not work well in that case because anonymous structures lack a recognizable name.

An alternative technique involves using the TYPE keyword to the WIDGET_INFO function. This method is useful when the widget event name does not specify the widget from which the event originated. Timer events are an example; although the events originate from a widget, the event structure’s name is WIDGET_TIMER. The following statement checks to see if the event is a timer event and, if it is, prints the type code of the widget that generated the event.

IF ((TAG_NAMES(EVENT, /STRUCTURE) EQ 'WIDGET_TIMER') THEN $

  PRINT, WIDGET_INFO(EVENT.ID, /TYPE)

Such a check would be useful if a given widget could generate either a timer event or a “normal” event, and you wanted to differentiate between the two.

Note: Always check for a distinct type of widget event. IDL will continue to add new widgets with new event structures, so it is important not to make assumptions about the contents of a random widget event structure. The structure of existing widget events will remain stable, (although new fields may be added) so checking for a particular type of widget event will always work.

Keyboard Focus Events

Base, table, and text widgets can be set to generate keyboard focus events. Generating and examining keyboard focus events allows you to determine when a given widget has either gained or lost the keyboard focus—that is, when it is brought to the foreground or when it is covered by another window.

Set the KBRD_FOCUS_EVENTS keyword to WIDGET_BASE, WIDGET_TABLE, or WIDGET_TEXT to generate keyboard focus events. (You can also modify an existing base, table, or text widget to generate keyboard focus events using the KBRD_FOCUS_EVENTS keyword to WIDGET_CONTROL.) You can then use your event-handling procedure to cache the widget ID of the last widget (with keyboard focus events enabled) to have the keyboard focus. One situation where this is useful is when you have an application menu (created with the MBAR keyword to WIDGET_BASE) and you wish to perform an action in a text widget based on the menu item selected. Although the event generated by the user’s menu selection has the menu’s base as its top-level widget ID, if you generate and track keyboard focus events for the text widget, you can “remember” which widget the action triggered by the menu selection should affect. Note that in this example, keyboard focus events are not generated for the menubar’s base.

Timer Events

In addition to the normal widget events discussed previously, IDL allows the user to make timer event requests by using the TIMER keyword. Such events are useful in many applications that are time dependent, such as animation. The syntax for making such a request is:

WIDGET_CONTROL, Widget_Id, TIMER=interval_in_seconds

Widget_Id can be the ID of any type of widget. When such a request is made, IDL generates a timer request after the requested time interval has passed. Timer events consist of a structure with only the standard three fields; no additional information is provided.

Note: At most one timer event request can be associated with a given widget ID. If multiple timer event requests are associated with a single widget, the last request made takes precedence.

It is up to the programmer to differentiate between a normal event and a timer event for a given widget. One way to solve this problem is to make timer requests for widgets that do not otherwise generate events, such as base or label widgets.

Each timer request causes a single event to be generated. To generate a steady stream of timer events, you must make a new timer request in the event handler routine each time a timer event is delivered. The following example demonstrates how to check for a timer event and generate a new timer event each time a timer event occurs:

PRO timer_example_event, ev

 

  WIDGET_CONTROL, ev.ID, GET_UVALUE=uval

  IF (TAG_NAMES(ev, /STRUCTURE_NAME) EQ 'WIDGET_TIMER') THEN BEGIN

    PRINT, 'Timer Fired'

    WIDGET_CONTROL, ev.TOP, TIMER=2

  ENDIF

 

  CASE uval OF

    'timer' : BEGIN

      WIDGET_CONTROL, ev.TOP, TIMER=2

      END

    'exit' : WIDGET_CONTROL, ev.TOP, /DESTROY

  ELSE:

  ENDCASE

 

END

 

PRO timer_example

  base = WIDGET_BASE(/COLUMN, UVALUE='base')

  b1 = WIDGET_BUTTON(base, VALUE='Fire event', UVALUE='timer')

  b2 = WIDGET_BUTTON(base, VALUE='Exit', UVALUE='exit')

  WIDGET_CONTROL, base, /REALIZE

  XMANAGER, 'timer_example', base, /NO_BLOCK

END

See Draw Widget Example for a larger example using timer events.

Tracking Events

Tracking events allow you to determine when the mouse pointer has entered or left the area of the computer screen covered by a given widget. You can use tracking events to allow your interface to react as the user moves the mouse pointer over different interface elements. Tracking events are generated for a widget when the widget creation routine is called with the TRACKING_EVENTS keyword set.

The event structure of a tracking event includes a field named ENTER that contains a 1 (one) if the mouse pointer entered the region covered by the widget, or 0 (zero) if the mouse pointer left the region covered by the widget. The following example demonstrates how to check for tracking events and modify the value of a button widget when the mouse cursor is positioned over it.

PRO tracking_demo_event, event

  IF (TAG_NAMES(event, /STRUCTURE_NAME) EQ 'WIDGET_TRACKING') $

  THEN BEGIN

    IF (event.ENTER EQ 1) THEN BEGIN

      WIDGET_CONTROL, event.ID, SET_VALUE='Press to Quit'

    ENDIF ELSE BEGIN

      WIDGET_CONTROL, event.ID, $

        SET_VALUE='What does this button do?'

    ENDELSE

  ENDIF ELSE BEGIN

    WIDGET_CONTROL, event.TOP, /DESTROY

  ENDELSE

END

 

PRO tracking_demo

  base = WIDGET_BASE(/COLUMN)

  button = WIDGET_BUTTON(base, $

    VALUE='What does this button do?', /TRACKING_EVENTS)

  WIDGET_CONTROL, base, /REALIZE

  XMANAGER, 'tracking_demo', base

END

Context Menu Events

Base, list, text, table and tree widgets can be set to generate context menu events. Generating and examining context menu events allows you to determine when the user has clicked the right-hand mouse button over a given widget, which in turn allows you to display a “context menu.” (Draw widgets can also generate events when the right-hand mouse button is clicked, using the general BUTTON_EVENTS mechanism.) See Context-Sensitive>Menus for a detailed description.