Creating Event Handlers

Widget event handlers manage widget actions. The WIDGET_CONTROL routine is the key to manipulating widgets, so you will use it in every event handler. After the widget is created, WIDGET_CONTROL is the way you control widget actions. See WIDGET_CONTROL to understand all the uses for this routine.

You will need to add event handlers to control the different actions in your widget application. Before you do that, you will need to set up a structure to hold the information that the event handlers need.

Tip
Widget event-handling routines can be placed in the same file as the primary routine or in a separate file.

State Structures

Also known as an information structure, the fields in the state structure hold the widget variables that are called by events. The main purpose of this structure is to pass information from one part of the program to another. State structures also store information when variable values change.

In this widget example program, when you choose an image from the droplist, the event handler changes the definition of the image variable and stores it in the state structure.

A state structure is usually defined in the primary procedure and passed to the event handlers.

In this example, you will place the event routines in the same file, above the basic_draw_doc procedure.

The variables that go into the state structure for this program are:

Table 8-2: State Structure Variables

Variable
Definition

image

The image to display in the draw widget. This variable is defined as a pointer because its value changes, depending on the selection from the droplist. For more on pointers, see IDL Pointers.

draw

The draw widget ID.

width

The width of the base widget. The resize event handler can change this value and store it in the state structure.

height

The height of the base widget. The resize event handler can change this value and store it in the state structure.

base2_ysize

The current size of the base2 widget base, which contains the label, droplist, and button widgets. This is used to calculate the geometry of the base2 widget so that the draw widget resizes correctly.

Creating the State Structure

  1. Find the following line in your basic_draw_doc code:
  2. WSET, drawID  
    
  3. The following code define the state structure. Place this code just after the WSET line in your code:
  4. ; Define the state structure. 
    state = { image:Ptr_New(image), $ 
                draw:draw, $ 
                width:200, $ 
                height:200, $ 
                base2_ysize:base2_ysize $
             }  
     
    

The events that control the action of the widget will use and modify the elements in the state structure.

You can use different names for the fields and the variables, but you can also use the same names for both to avoid confusion. This example uses the same names for the structure fields and the widget variables.

See Creating and Defining Structures for more information.

Creating the Resize Event Handler

You can resize the base widget even before you add the resize event. But without the resize event, the draw widget and image will not resize.

This resize event gets the x and y sizes of the top-level base widget, and uses those dimensions to resize the draw widget. Inside the draw widget, the selected image resizes to fit the new dimensions of the draw widget.

Along with adding this event handler, you will add a call to the XMANAGER procedure. XMANAGER provides event management. XMANAGER takes control of event processing until all widgets have been destroyed. For more information, see XMANAGER.

If the EVENT_HANDLER keyword to XMANAGER is not supplied for a routine, IDL looks for an event handler with the same name as the widget creation routine, with the suffix _event. We will name this resize event handler basic_draw_doc_event.

Place the following routine at the beginning of the file, above the primary routine, basic_draw_doc.

; Resize event handler 
PRO basic_draw_doc_event, event 
 
  ; Get the state structure. Use WIDGET_CONTROL 
  ; to copy the state structure into a 
  ; local "state" variable. 
  WIDGET_CONTROL, event.top, GET_UVALUE=state 
   
  ; The new x and y sizes of the base widget are stored 
  ; in the event.x and event.y variables. The size of the 
  ; base widget defines the width and height of the 
  ; draw window (event.x and event.y) 
  state.width=event.x 
  state.height=event.y 
   
  ; Resize the draw widget. The widgetID for the draw widget 
  ; is taken from the state structure (state.draw). 
  ; The DRAW_XSIZE and DRAW_YSIZE keywords to WIDGET_CONTROL 
  ; resize the draw widget, based on the new x and y sizes 
  ; (event.x and event.y). The size of the base2 widget, 
  ; (which contains the controls) is subtracted 
  ; so that the draw window resizes correctly. 
  WIDGET_CONTROL, state.draw, Draw_XSize=event.x,  $ 
    Draw_YSize=event.y - state.base2_ysize 
     
  ; Use CONGRID to resize the image based on the 
  ; top-level base widget's size, taken from the 
  ; state structure. The /CENTER keyword centers the image. 
  TV, CONGRID(*(state.image), event.x, $ 
    event.y - state.base2_ysize, /CENTER) 
     
  ; Store the state structure. 
  WIDGET_CONTROL, event.top, SET_UVALUE=state 
END 

In addition to adding this routine, you must also add another WIDGET_CONTROL line to set the user value to the state structure. After the WIDGET_CONTROL line, you will add the XMANAGER command.

  1. Find the last line in the basic_draw_doc routine:
  2. END 
    
  3. Immediately above the END line (the last line in the file), add the following line:
  4.   WIDGET_CONTROL, base, SET_UVALUE=state 
    
  5. After the WIDGET_CONTROL line you just added and above the END line, add the following comments and the XMANAGER routine to look like the following:
  6.   ; Register the top-level base widget with 
      ; XMANAGER by providing the name of the widget program 
      XMANAGER, 'basic_draw_doc', base 
    
  7. Save the file, compile and run it.

You can now resize the widget. Place the mouse on any side or corner of the window until the resize arrow appears. Click and drag the corner to make the window the size you want it.

resize.gif

In the next sections, you will add the event handlers for the droplist and button widgets.

Tip
Close the widget example by clicking the close (X) button at the top of the window. The Done button will not work until we add that event handler in Creating the Done Button Event Handler.

See Widget Resizing for more information.

Creating the Droplist Event Handler

The next step in developing this widget is to add the droplist event handler.

The droplist allows you to choose from a list of different images to display in the draw widget. The name of the current image is shown in the droplist, changing only when you make a new selection.

Place the following droplist event handling procedure just after the resize event handler we just completed and above the primary routine, basic_draw_doc.

; Event handler for the droplist widget. 
PRO basic_draw_doc_droplist, event 
 
  ; Get the state structure. Use WIDGET_CONTROL 
  ; to copy the state structure into a 
  ; local "state" variable. 
  WIDGET_CONTROL, event.TOP, GET_UVALUE=state 
   
  ; The case statement lists the images to choose from 
  ; in the dropdown list.  Users can select only one of 
  ; these options. The case statement begins with CASE 
  ; and concludes with ENDCASE. Each image file corresponds 
  ; to the names listed in the "select" variable in basic_draw_doc. 
  CASE event.index OF 
    0 : BEGIN 
      file = FILEPATH('pdthorax124.jpg', $ 
        SUBDIR=['examples', 'data']) 
    END 
    1 : BEGIN 
      file = FILEPATH('md1107g8a.jpg', $ 
        SUBDIR=['examples', 'data']) 
    END 
    2 : BEGIN 
      file = FILEPATH('md5290fc1.jpg', $ 
        SUBDIR=['examples', 'data']) 
    END 
  ENDCASE 
   
  ; Define the image variable to read the image selected. 
  image = READ_IMAGE(file) 
   
  ; Store the new image selection back into the state structure. 
  *(state.image)=image 
   
  ; Set the user value to state. 
  WIDGET_CONTROL, event.top, SET_UVALUE=state 
   
  ; Use CONGRID to resize the image based on the 
  ; top-level base widget's size, taken from the 
  ; state structure. The /CENTER keyword centers the image. 
  ; If the draw widget has not been resized or if its 
  ; height is less than or equal to 200, use the 
  ; first CONGRID command. 
  ; If the draw widget has been resized, use the 
  ; other CONGRID command that calculates the size 
  ; of the base2 widget. 
   
  IF state.height LE 200 then BEGIN 
    TV, CONGRID(*(state.image), state.width, $ 
      state.height, /CENTER) 
  ENDIF ELSE BEGIN 
    TV, CONGRID(*(state.image), state.width, $ 
      state.height - state.base2_ysize, /CENTER) 
  ENDELSE 
END 
 

In addition to adding this event handling routine, you must also add some information to the WIDGET_DROPLIST definition, telling it which event handler to use.

  1. Find the following lines in the basic_draw_doc routine:
  2. drop = WIDGET_DROPLIST(base2, VALUE=select) 
    
  3. At the end of the line, but before the parenthesis, add a comma and EVENT_PRO='basic_draw_doc_done' so that the lines now read:
  4.   drop = WIDGET_DROPLIST(base2, VALUE=select, $ 
        EVENT_PRO='basic_draw_doc_droplist') 
    
  5. Save the file, compile and run it.

You can now choose images from the droplist and resize the widget. The example below shows one of the selections from the dropdown.

select.gif

In the next sections, we will add the event handlers for closing the widget and cleaning up the pointers.

Tip
Close the widget example by clicking the close (X) button at the top of the window. The Done button will not work until you add that event handler in the next section.

For another droplist example, see Draw Widget Example.