Example: A Simple UI Panel

The following example creates a simple user interface panel consisting of two buttons: Rotate and Hide/Show. The Rotate button rotates the selected iTool component 90 degrees, if possible. The Hide/Show button toggles the value of the HIDE property of the selected object.

Figure 14-1: The example panel.

panel.gif

Note
This example is intended to demonstrate the concepts involved in creating a user interface panel. For examples of more useful panels, see the files idlitwdimgmenu.pro and idlitwdvolmenu.pro, which create the user interface panels for the IIMAGE and IVOLUME iTools, respectively. Both files are located in the lib/itools/ui_widgets subdirectory of the IDL installation directory.

To display a user interface panel named Example4_panel, this example creates the following items:

Example Code
The code for this example user interface panel is included in the file example4_panel.pro in the examples/doc/itools subdirectory of the IDL distribution. Run the example procedure by entering example4_panel at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT example4_panel.pro.

Panel Creation Routine

The user interface panel creation routine (beginning with the line PRO Example4_panel, wPanel, oUI) does the work of displaying the IDL widgets that make up the UI panel display.

PRO Example4_panel, wPanel, oUI 
 
   ; Set the title used on the panel's tab. 
   WIDGET_CONTROL, wPanel, BASE_SET_TITLE = 'Example Panel' 
 
   ; Specify the event handler 
   WIDGET_CONTROL, wPanel, EVENT_PRO = "Example4_panel_event" 
 
   ; Register the panel with the user interface object. 
   strObserverIdentifier = oUI->RegisterWidget(wPanel, "Panel", $ 
      'Example4_panel_callback') 
   ; Register to receive selection events on visualizations. 
   oUI->AddOnNotifyObserver, strObserverIdentifier, $ 
      'Visualization' 
    
   ; Retrieve a reference to the current iTool. 
   oTool = oUI->GetTool() 
 
   ; Create a base widget to hold the contents of the panel. 
   wBase = WIDGET_BASE(wPanel, /COLUMN, SPACE = 5, /ALIGN_LEFT) 
    
   ; Create panel contents. 
   wLabel = WIDGET_LABEL(wBase, VALUE = "Choose an Action:", $ 
      /ALIGN_LEFT) 
    
   ; Get the Operation ID of the rotate operation. If the operation 
   ; exists, create the "Rotate Item" button and monitor whether 
   ; the operation is available for the selected item. 
   opID = 'Operations/Operations/Rotate/RotateLeft' 
   oRotate = oTool->GetByIdentifier(opID) 
 
   IF (OBJ_VALID(oRotate)) THEN BEGIN 
      idRotate = oRotate->GetFullIdentifier() 
      wRotate = WIDGET_BUTTON(wBase, VALUE = "Rotate Item", $ 
         UVALUE="ROTATE") 
      ; Monitor for availablity of the Rotate operation. 
      oUI->AddOnNotifyObserver, strObserverIdentifier, idRotate 
   ENDIF ELSE $ 
      idRotate = 0 
 
   wHide = WIDGET_BUTTON(wBase, VALUE = "Show/Hide Item", $ 
      UVALUE = "HIDE") 
 
   ; Pack up the state structure and store in first child. 
   state = {oTool:oTool, $          
            oUI:oUI, $ 
            idRotate : idRotate, $ 
            wPanel:wPanel, $ 
            wBase:wBase, $ 
            wRotate:wRotate, $ 
            wHide:wHide $ 
          } 
   wChild = WIDGET_INFO(wPanel, /CHILD) 
 
   IF wChild NE 0 THEN $ 
      WIDGET_CONTROL, wChild, SET_UVALUE = state, /NO_COPY 
 
END 

Discussion

It is beyond the scope of this chapter to describe the IDL widget concepts employed in the Example4_panel example; the comments in the code that creates the user interface panel describe most of the features. The following points are worth noting, however:

Panel Event Handler Routine

The event-handler routine (beginning with the line PRO Example4_panel_event, event) receives widget events generated by the widgets that make up the user interface panel, and acts accordingly.

PRO Example4_panel_event, event 
 
   ; Retrieve the widget ID of the first child widget of 
   ; the UI panel. 
   wChild = WIDGET_INFO(event.handler, /CHILD) 
 
   ; Retrieve the state structure from the user value of 
   ; the first child widget. 
   WIDGET_CONTROL, wChild, GET_UVALUE = state 
 
   ; Retrieve the user value of the widget that generated 
   ; the event. 
   WIDGET_CONTROL, event.id, GET_UVALUE = uvalue 
 
   ; Now do the work for each panel item. 
   SWITCH STRUPCASE(uvalue) OF 
      'ROTATE': BEGIN 
         ; Apply the Rotate Left operation to the selected item. 
         success = state.oUI->DoAction(state.idRotate) 
         RETURN 
      END 
      'HIDE': BEGIN 
         ; Hide the selected item. 
         ; 
         oTargets = state.oTool->GetSelectedItems(count = nTarg) 
         IF nTarg GT 0 THEN BEGIN 
            ; If there are selected items, use only the last item 
            ; selected, which is stored in the first element of 
            ; the returned array. 
            oTarget = oTargets[0] 
            ; Get the iTool identifier of the selected item. 
            name = oTarget->GetFullIdentifier() 
            ; Retrieve the setting of the HIDE property. 
            oTarget->GetProperty, HIDE = hide 
            ; Change the value of the HIDE property from 0 to 1 
            ; or from 1 to 0. Use the DoSetProperty and 
            ; CommitActions method to ensure that the change 
            ; is entered into the undo/redo transaction buffer. 
            void = state.oTool->DoSetProperty(name, "HIDE", $ 
               ((hide+1) MOD 2)) 
            state.oTool->CommitActions 
         ENDIF 
         BREAK 
      END 
      ELSE: 
   ENDSWITCH 
 
   ; Refresh the iTool window. 
   state.oTool->RefreshCurrentWindow 
 
END 

Discussion

It is beyond the scope of this chapter to describe the IDL widget concepts employed in the Example4_panel event handler; the comments in the code describe most of the features. The following points are worth noting, however:

Panel Callback Routine

The user interface panel callback routine is called whenever a component, for which an OnNotifyObserver has been registered, generates a message. It parses the message received and takes action as necessary.

PRO Example4_panel_callback, wPanel, strID, messageIn, component 
 
   ; Make sure we have a valid widget ID. 
   IF ~ WIDGET_INFO(wPanel, /VALID) THEN RETURN 
 
   ; Retrieve the widget ID of the first child widget of 
   ; the UI panel. 
   wChild = WIDGET_INFO(wPanel, /CHILD) 
 
   ; Retrieve the state structure from the user value of 
   ; the first child widget. 
   WIDGET_CONTROL, wChild, GET_UVALUE = state 
    
   ; Process as necessary, depending on the message received. 
   SWITCH STRUPCASE(messageIn) OF 
    
      ; This section handles messages generated when the rotate 
      ; operation becomes available or unavailable, and sensitizes 
      ; or desensitizes the "Rotate" button accordingly. 
      'SENSITIVE': 
      'UNSENSITIVE': BEGIN 
         WIDGET_CONTROL, state.wRotate, $ 
            SENSITIVE = (messageIn EQ 'SENSITIVE') 
         BREAK 
      END 
 
      ; This section handles messages generated when the 
      ; item selected in the iTool window changes and changes 
      ; the sensitivity of the "Hide/Show" and "Rotate" buttons 
      ; accordingly. 
      'SELECTIONCHANGED': BEGIN 
         ; Retrieve the item that was selected last, which is 
         ; stored in the first element of the returned array. 
         oSel = state.oTool->GetSelectedItems() 
         oSel = oSel[0] 
         ; If the last item selected is not a visualization,  
         ; desensitize the "Hide/Show" and "Rotate" buttons. 
         IF (~OBJ_ISA(oSel, 'IDLITVISUALIZATION')) THEN BEGIN 
            WIDGET_CONTROL, state.wHide, SENSITIVE = 0 
            WIDGET_CONTROL, state.wRotate, SENSITIVE = 0 
         ENDIF ELSE BEGIN 
         ; If the selected object is a visualization, sensitize 
         ; the "Hide/Show" and "Rotate" buttons. 
            WIDGET_CONTROL, state.wHide, SENSITIVE = 1 
            WIDGET_CONTROL, state.wRotate, SENSITIVE = 1 
         ENDELSE 
         BREAK 
      END 
      ELSE: 
   ENDSWITCH 
    
END 

Discussion

The example panel's callback routine performs the following tasks:

Panel Type Specification

In order to display the Example4_panel user interface panel along with an iTool, the following two things must happen:

  1. The UI panel must be registered, using the IREGISTER procedure.
  2. A tool with the appropriate TYPE must be created.

For the purposes of this example, we will create an iTool named example4tool, with a launch routine named example4tool.pro, and an iTool object definition routine named example4tool__define.pro.

Example Code
Both example4tool.pro, and example4tool__define.pro are included in the examples/doc/itools subdirectory of the IDL distribution. Run these example procedures by entering example4tool or example4tool__define at the IDL command prompt or view the files in an IDL Editor window by entering .EDIT example4tool.pro or .EDIT example4tool__define.pro.

In the example4tool.pro file, we included the following statement:

IREGISTER, 'Example Panel', 'Example4_panel', TYPE = 'EXAMPLE', $ 
   /UI_PANEL 

Setting the TYPE keyword equal to the string EXAMPLE specifies that the panel should be displayed for all iTools of this type.

In the example4tool__define.pro file, we include the string EXAMPLE in the TYPE property specified in the Init method:

FUNCTION example4tool::Init, _REF_EXTRA = _extra 
 
IF (self->IDLitToolbase::Init(_EXTRA = _extra, $ 
                               TYPE = 'EXAMPLE') EQ 0) $ 
THEN RETURN, 0 

Since the TYPE specified for the user interface panel in the call to IREGISTER matches the TYPE defined for our example iTool class, calling the launch routine example4tool at the IDL Command Line creates a new iTool and displays the Example4_panel panel on the right side of the iTool window.