Example: a Custom iTool Interface

This example creates a custom iTool interface that incorporates several standard IDL widgets to the left of the drawable area and displays a subset of the menus and toolbars that appear in a standard iTool. A button widget inserts a plot line created from random data, and several controls allow the user to change the number of points used to create the line, the line thickness, and the line color. Finally, a button launches an iTool operation that affects the selected plot data. The finished interface looks like this:

Figure 15-1: Example Custom iTool Interface

custom_interface.gif

The example is purposefully simple. All of the actions accomplished by the custom interface can be accomplished using the standard iTool interface. It does, however, illustrate the concepts necessary to create a custom iTool interface.

This example consists of three files, described in the following sections:

Widget Interface Creation Routine

This section describes the widget interface creation routine for the example interface.

Example Code
The example consists of several routines and is quite long. As a result, this discussion deals with individual chunks and may skip briefly over sections that have more to do with widget programming and are not explicitly related to the creation of an iTool interface. To see the routine in its entirety, inspect the file example2_wdtool.pro in the examples/doc/itools subdirectory of the IDL distribution. Run the example procedure by entering example2_wdtool at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT example2_wdtool.pro.

Individual routines in the interface definition are discussed here in the order they appear in the source file. The routines are:

In our interface definition, we store the state structure for the entire widget interface in a pointer (named pState) that is itself stored in the user value of the first child widget of the top-level base widget. This is a standard technique that allows us to pass information about the interface between the interface routines. (Handling of widget state information is discussed in detail in Managing Application State and Creating a Compound Widget (User Interface Programming).) If you are not familiar with this concept, inspect the example2_wdtool routine before reading the event handling and callback routines.

Note
We store our state variable in the user value of the first child widget, rather than the user value of the top-level base, as a matter of programming style. You could also choose to store the variable in the user value of the top-level base.

example2_wdtool_callback

Our example interface handles only one message from the iTool system: FILENAME. The complete code for the callback routine is shown below.

PRO example2_wdtool_callback, wBase, strID, messageIn, userdata 
 
   ; Make sure we have a valid widget. 
   IF (~WIDGET_INFO(wBase, /VALID)) THEN $ 
      RETURN 
 
   ; Retrieve a pointer to the state structure. 
   wChild = WIDGET_INFO(wBase, /CHILD) 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
 
   ; Handle the message that was passed in. 
   CASE STRUPCASE(messageIn) OF 
 
      ; The FILENAME message is received if the user saves 
      ; the iTool with a new name. This callback sets the 
      ; title of the iTool to match the name of the file. 
      'FILENAME': BEGIN 
         ; Use the new filename to construct the title. 
         ; Remove the path. 
         filename = FILE_BASENAME(userdata) 
         ; Append the filename onto the base title. 
         newTitle = (*pState).title + ' [' + filename + ']' 
         WIDGET_CONTROL, wBase, TLB_SET_TITLE = newTitle 
      END 
 
      ; Other messages would be handled here. 
       
      ELSE:  ; Do nothing 
 
   ENDCASE 
 
END 

Discussion

The FILENAME message and the rest of the callback routine are discussed in Example Callback Routine.

example2_wdtool_resize

The widget resizing routine for our example interface is shown below. It accepts three arguments: a pointer to the widget interface state structure, an integer representing the change in width (in pixels), and an integer representing the change in height (also in pixels).

Note
Widget resizing code depends almost entirely on the structure and layout of the widget interface you are creating. While this example may give you ideas about how to resize your interface, you will need to change it — probably substantially — to suit the needs of your interface.

PRO example2_wdtool_resize, pState, deltaW, deltaH 
 
   ; Retrieve the original geometry (prior to the resize) 
   ; of the iTool draw and toolbar widgets. 
   drawgeom = WIDGET_INFO((*pState).wDraw, /GEOMETRY) 
   toolbarGeom = WIDGET_INFO((*pState).wToolbar, /GEOMETRY) 
 
   ; Compute the updated dimensions of the visible portion 
   ; of the draw widget. 
   newVisW = (drawgeom.xsize + deltaW) 
   newVisH = (drawgeom.ysize + deltaH) 
 
   ; Check whether UPDATE is turned on, and save the value. 
   isUpdate = WIDGET_INFO((*pState).wBase, /UPDATE) 
    
   ; Under Unix, UPDATE must be turned on or windows will 
   ; not resize properly. Turn UPDATE off under Windows 
   ; to prevent window flashing. 
   IF (!VERSION.OS_FAMILY EQ 'Windows') THEN BEGIN 
      IF (isUpdate) THEN $ 
         WIDGET_CONTROL, (*pState).wBase, UPDATE = 0 
   ENDIF ELSE BEGIN 
      ; On Unix make sure update is on. 
      IF (~isUpdate) THEN $ 
         WIDGET_CONTROL, (*pState).wBase, /UPDATE 
   ENDELSE 
 
   ; Update the draw widget dimensions. 
   IF (newVisW NE drawgeom.xsize || newVisH ne drawgeom.ysize) $ 
      THEN BEGIN 
      CW_ITWINDOW_RESIZE, (*pState).wDraw, newVisW, newVisH 
   ENDIF 
 
   ; Update the width of the toolbar base. 
   WIDGET_CONTROL, (*pState).wToolbar, $ 
      SCR_XSIZE = toolbarGeom.scr_xsize+deltaW 
 
   ; Update the status bar to be the same width as the toolbar. 
   CW_ITSTATUSBAR_RESIZE, (*pState).wStatus, $ 
      toolbarGeom.scr_xsize+deltaW 
 
   ; Turn UPDATE back on if we turned it off. 
   IF (isUpdate && ~WIDGET_INFO((*pState).wBase, /UPDATE)) THEN $ 
      WIDGET_CONTROL, (*pState).wBase, /UPDATE 
 
   ; Retrieve and store the new top-level base size. 
   IF (WIDGET_INFO((*pState).wBase, /REALIZED)) THEN BEGIN 
      WIDGET_CONTROL, (*pState).wBase, TLB_GET_SIZE = basesize 
      (*pState).basesize = basesize 
   ENDIF 
 
END 

Discussion

Our code resizes only three widgets when the size of the top-level base changes: the iTool window, the toolbar, and the status bar. The toolbar and status bar are resized to fit the new width of the top-level base, and the iTool window is made larger or smaller by the same amount as the top-level base. This preserves the overall arrangement of the interface elements, and does not change the width of the left-hand base, which holds the "custom" interface elements.

Note the handling of the UPDATE keyword. This is necessary because UNIX and Microsoft Windows behave differently as the top-level base is being resized.

Note also that we use the CW_ITWINDOW_RESIZE and CW_ITSTATUSBAR_RESIZE procedures to resize the iTool window and status bar widgets. These routines handle the details of internal resizing of the compound widgets, and perform other necessary adjustments. The width of the toolbar is resized in a more traditional way, by setting the SCR_XSIZE on the base widget that holds the individual toolbars.

Finally, we store the new size of the top-level base in the basesize field of the widget interface's state structure. Storing this value in the state structure allows us to calculate the change in size of the top-level base in when the WIDGET_BASE event arrives in our event-handler routine.

example2_wdtool_cleanup

The cleanup routine for our interface is simple; it frees the pointer used to hold the widget interface's state structure. The complete code for the cleanup routine is shown below.

PRO example2_wdtool_cleanup, wChild 
 
   ; Make sure we have a valid widget ID. 
   IF (~WIDGET_INFO(wChild, /VALID)) THEN $ 
      RETURN 
 
   ; Retrieve the pointer to the state structure, and 
   ; free it. 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
   IF (PTR_VALID(pState)) THEN $ 
      PTR_FREE, pState 
 
END 

Discussion

Note that this routine is only called when the widget interface is actually destroyed, not when the WIDGET_KILL_REQUEST event is processed. See Handling Shutdown Events for details.

example2_wdtool_event

The main event-handling routine for our widget interface handles three types of events that might be generated by the top-level base widget:

A more complicated interface may handle additional events; the techniques used would be similar to those illustrated here. The complete code for the main event-handler routine is shown below.

PRO example2_wdtool_event, event 
 
   ; Retrieve a pointer to the state structure. 
   wChild = WIDGET_INFO(event.handler, /CHILD) 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
 
   CASE TAG_NAMES(event, /STRUCTURE_NAME) OF 
 
   ; Destroy the widget. 
   'WIDGET_KILL_REQUEST': BEGIN 
      ; Get the shutdown service and call DoAction. 
      ; This code must be here, and not in the _cleanup routine, 
      ; because the tool may not actually be killed. (For example 
      ; the user may be asked if they want to save, and they may  
      ; hit "Cancel" instead.) 
      IF OBJ_VALID((*pState).oUI) THEN BEGIN 
         oTool = (*pState).oUI->GetTool() 
         oShutdown = oTool->GetService('SHUTDOWN') 
         void=(*pState).oUI->DoAction(oShutdown->getFullIdentifier()) 
      ENDIF 
   END 
 
   ; Focus change. 
   'WIDGET_KBRD_FOCUS': BEGIN 
      ; If the iTool is gaining the focus, Get the set current tool 
      ; service and call DoAction. 
      IF (event.enter && OBJ_VALID((*pState).oUI)) THEN BEGIN 
         oTool = (*pState).oUI->GetTool() 
         oSetCurrent = oTool->GetService('SET_AS_CURRENT_TOOL') 
         void = oTool->DoAction(oSetCurrent->GetFullIdentifier()) 
      ENDIF 
   END 
 
   ; The top-level base was resized. 
   'WIDGET_BASE': BEGIN 
      ; Compute the size change of the base relative to 
      ; its cached former size. 
      WIDGET_CONTROL, event.top, TLB_GET_SIZE = newSize 
      deltaW = newSize[0] - (*pState).basesize[0] 
      deltaH = newSize[1] - (*pState).basesize[1] 
      example2_wdtool_resize, pState, deltaW, deltaH 
      END 
 
   ELSE: ; Do nothing 
 
   ENDCASE 
 
END 

Discussion

Two of the three events handled in this routine are discussed in earlier sections of this chapter. See Handling Resize Events for details on the WIDGET_BASE event and Handling Shutdown Events for details on the WIDGET_KILL_REQUEST event.

The WIDGET_KBRD_FOCUS event arrives when the user clicks "into" or "out of" the widget interface. We are concerned only with events generated when the user selects the widget interface, because in this case we need to inform the iTool system object that our iTool has become the "current" tool. To do this, we check the value of the enter field of the widget event structure; if it contains a 1 (one), we know that the user has clicked "into" the interface.

Next, we check to make sure that the user interface object stored in the oUI field of the widget interface state structure is still valid. If the object is valid, we retrieve a reference to the iTool object using the user interface object's GetTool method. We use the iTool object reference to retrieve an object reference to the SET_AS_CURRENT_TOOL service, and call the iTool object's DoAction method with the full identifier of the service.

draw_plot_event

The draw_plot_event routine is specified as the event handler for the "Insert New Plot" button in the custom section of the interface. The routine checks the values of the other widgets in the custom interface and uses the IPLOT routine to generate a new plot line in our iTool window. The complete code for this event-handler routine is shown below.

PRO draw_plot_event, event 
 
   ; Retrieve a pointer to the state structure. 
   wChild = WIDGET_INFO(event.top, /CHILD) 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
 
   ; Get the iTool identifier and make sure our iTool 
   ; is the current tool. 
   toolID = (*pState).oTool->GetFullIdentifier() 
   ISETCURRENT, toolID 
 
   ; Define some line colors. 
   colors = [[0,0,0],[255,0,0], [0,255,0], [0,0,255]] 
 
   ; Get the value of the line color droplist and use it 
   ; to select the line color. 
   linecolor = WIDGET_INFO((*pState).wLineColor, /DROPLIST_SELECT) 
   newcolor = colors[*,linecolor] 
 
   ; Get the value of the "number of points" slider. 
   WIDGET_CONTROL, (*pState).wSlider, GET_VALUE=points 
 
   ; Get the value of the line size droplist. 
   linesize = WIDGET_INFO((*pState).wLineSize, /DROPLIST_SELECT)+1 
 
   ; Call IPLOT to create a plot of random values, replacing the 
   ; data used in the iTool's window. 
   IPLOT, RANDOMU(seed, points), THICK=linesize, $ 
      COLOR=newcolor, VIEW_NUMBER=1 
 
END 

Discussion

This routine uses mostly standard widget programming techniques. Two points are worth noting, however:

  1. We must be sure that our iTool is set as the current tool. To do this, we retrieve our iTool's identifier using the object reference stored in the widget interface's state structure and the GetFullIdentifier method. Next, we use the ISETCURRENT routine with the full identifier to make sure our tool is current.
  2. When we call the IPLOT routine to generate the new plot, we set the VIEW_NUMBER keyword equal to 1 (one). This replaces the data in the first (and in our case, only) view in the tool with the data specified.

linesize_event

The linesize_event routine is specified as the event handler for the Line Size droplist in the custom section of the interface. The complete code for this event-handler routine is shown below.

PRO linesize_event, event 
 
   ; Retrieve a pointer to the state structure. 
   wChild = WIDGET_INFO(event.top, /CHILD) 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
    
   ; Get the iTool identifier and make sure our iTool 
   ; is the current tool. 
   toolID = (*pState).oTool->GetFullIdentifier() 
   ISETCURRENT, toolID 
 
   ; Get the value of the line size droplist. 
   linesize = WIDGET_INFO((*pState).wLineSize, /DROPLIST_SELECT)+1 
 
   ; Select the first plot line visualization in the window. 
   ; There should be only one line, but we select the first one 
   ; just to be sure. 
   plotID = (*pState).oTool->FindIdentifiers('*plot*', $ 
      /VISUALIZATIONS) 
   plotObj = (*pState).oTool->GetByIdentifier(plotID[0]) 
   plotObj->Select 
    
   ; Set the THICK property on the plot line and commit the change. 
   void = (*pState).oTool->DoSetProperty(plotID, 'THICK', $ 
      linesize) 
   (*pState).oTool->CommitActions 
 
END 

Discussion

This routine uses the same technique as the draw_plot_event routine to ensure that our iTool is the current tool. It then retrieves the identifier of the plot line, ensures that the line itself is selected, and sets the THICK property on the line. For additional information on retrieving component identifiers and changing property values, see Controlling iTools from the IDL Command Line.

color_event

The color_event routine is specified as the event handler for the Line Color droplist in the custom section of the interface. The complete code for this event-handler routine is shown below.

PRO color_event, event 
 
   ; Retrieve a pointer to the state structure. 
   wChild = WIDGET_INFO(event.top, /CHILD) 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
   
   ; Get the iTool identifier and make sure our iTool 
   ; is the current tool. 
   toolID = (*pState).oTool->GetFullIdentifier() 
   ISETCURRENT, toolID 
 
   ; Define some line colors. 
   colors = [[0,0,0],[255,0,0], [0,255,0], [0,0,255]] 
 
   ; Get the value of the line color droplist and use it 
   ; to select the line color. 
   linecolor = WIDGET_INFO((*pState).wLineColor, /DROPLIST_SELECT) 
   newcolor = colors[*,linecolor] 
 
   ; Select the first plot line visualization in the window. 
   ; There should be only one line, but we select the first one 
   ; just to be sure. 
   plotID = (*pState).oTool->FindIdentifiers('*plot*', $ 
      /VISUALIZATIONS) 
   plotObj = (*pState).oTool->GetByIdentifier(plotID[0]) 
   plotObj->Select 
    
   ; Set the COLOR property on the plot line and commit the change. 
   void = (*pState).oTool->DoSetProperty(plotID, 'COLOR', $ 
      newcolor) 
   (*pState).oTool->CommitActions 
 
END 

Discussion

This routine uses the same technique as the draw_plot_event routine to ensure that our iTool is the current tool. It then retrieves the identifier of the plot line, ensures that the line itself is selected, and sets the COLOR property on the line. For additional information on retrieving component identifiers and changing property values, see Controlling iTools from the IDL Command Line.

filter_event

The filter_event routine is specified as the event handler for the "Filter this Plot" button in the custom section of the interface. The complete code for this event-handler routine is shown below.

PRO filter_event, event 
 
   ; Retrieve a pointer to the state structure. 
   wChild = WIDGET_INFO(event.top, /CHILD) 
   WIDGET_CONTROL, wChild, GET_UVALUE = pState 
    
   ; Get the iTool identifier and make sure our iTool 
   ; is the current tool. 
   toolID = (*pState).oTool->GetFullIdentifier() 
   ISETCURRENT, toolID 
 
   ; Select the first plot line visualization in the window. 
   ; There should be only one line, but we select the first one 
   ; just to be sure. Also retrieve the identifier for the Median 
   ; filter operation. 
   plotID = (*pState).oTool->FindIdentifiers('*plot*', $ 
      /VISUALIZATIONS) 
   medianID = (*pState).oTool ->FindIdentifiers('*median', $ 
      /OPERATIONS) 
   plotObj = (*pState).oTool->GetByIdentifier(plotID[0]) 
   plotObj->Select 
    
   ; Apply the Median filter operation to the selected plot line 
   ; and commit the change. 
   void = (*pState).oTool->DoAction(medianID) 
   (*pState).oTool->CommitActions 
 
END 

Discussion

This routine uses the same technique as the draw_plot_event routine to ensure that our iTool is the current tool. It then retrieves the identifier of the plot line and the Median operation, selects the line, calls the DoAction method to apply the Median filter to the selected plot line. For additional information on retrieving component identifiers and executing operations, see Controlling iTools from the IDL Command Line.

example2_wdtool

The example2_wdtool routine builds the widget hierarchy for our custom iTool interface and registers it with the iTool system. Much of this routine consists of standard IDL widget programming, and many of the sections have been discussed in Creating the Interface Routine. The complete code for the widget creation routine is shown below.

PRO example2_wdtool, oTool, TITLE = titleIn, $ 
         LOCATION = location, $ 
         VIRTUAL_DIMENSIONS = virtualDimensions, $ 
         USER_INTERFACE = oUI, $  ; output keyword 
         _REF_EXTRA = _extra 
 
   ; Make sure the iTool object reference we've been passed 
   ; is valid. 
   IF (~OBJ_VALID(oTool)) THEN $ 
      MESSAGE, 'Tool is not a valid object.' 
 
   ; Set the window title. 
   title = (N_ELEMENTS(titleIn) GT 0) ? titleIn[0] : 'IDL iTool' 
 
   ; Display the hourglass cursor while the iTool is loading. 
   WIDGET_CONTROL, /HOURGLASS 
 
   ; Create a base widget to hold everything. 
   wBase = WIDGET_BASE(/COLUMN, MBAR = wMenubar, $ 
      TITLE = title, $ 
      /TLB_KILL_REQUEST_EVENTS, $ 
      /TLB_SIZE_EVENTS, $ 
      /KBRD_FOCUS_EVENTS, $ 
      _EXTRA = _extra) 
 
   ; Create a new user interface object, using our iTool. 
   oUI = OBJ_NEW('IDLitUI', oTool, GROUP_LEADER = wBase) 
 
   ; Menubars: 
   ; iTool menubars are created using the CW_ITMENU compound 
   ; widget. The following statements create the standard iTool 
   ; menus, pointing at the standard iTool operations containers. 
   ; Note that if the iTool to which this user interface is applied 
   ; has registered new operations in these containers, those 
   ; operations will show up automatically. Similarly, if the 
   ; iTool has unregistered any operations in these containers, 
   ; the operations will not appear. Our example tool unregisters 
   ; several of the standard iTool menu items -- see the 
   ; 'example2tool__define.pro' file for examples. Note that we 
   ; don't want the standard Help menu in our example interface, 
   ; so we don't include it here. 
   wFile       = CW_ITMENU(wMenubar, oUI, 'Operations/File') 
   wEdit       = CW_ITMENU(wMenubar, oUI, 'Operations/Edit') 
   wInsert     = CW_ITMENU(wMenubar, oUI, 'Operations/Insert') 
   wOperations = CW_ITMENU(wMenubar, oUI, 'Operations/Operations') 
   wWindow     = CW_ITMENU(wMenubar, oUI, 'Operations/Window') 
 
   ; You can create additional (non-iTool) menus in the 
   ; traditional way. The following lines would create an 
   ; additional menu with two menu items. Note that you 
   ; must explicitly handle events from non-iTool menus 
   ; in your event handler. 
   ; 
   ; newMenu = WIDGET_BUTTON(wMenubar, VALUE='New Menu') 
   ; newMenu1 = WIDGET_BUTTON(newMenu, VALUE='one') 
   ; newMenu2 = WIDGET_BUTTON(newMenu, VALUE='two') 
 
   ; Toolbars: 
   ; iTool toolbars are created using the CW_ITTOOLBAR compound 
   ; widget. The following statements create the standard iTool 
   ; toolbars. Note that if the iTool to which this user interface 
   ; is applied has registered new operations or manipulators in 
   ; the referenced containers, those operations or manipulators 
   ; will show up automatically. Similarly, if the iTool has 
   ; unregistered any items in these containers, the items will 
   ; not appear. Our example tool uses the standard operations 
   ; and manipulators, but only displays three of the six standard 
   ; toolbars. 
   wToolbar = WIDGET_BASE(wBase, /ROW, XPAD = 0, YPAD = 0, $ 
      SPACE = 7) 
   wTool2 = CW_ITTOOLBAR(wToolbar, oUI, 'Toolbar/Edit') 
   wTool3 = CW_ITTOOLBAR(wToolbar, oUI, 'Manipulators', $ 
      /EXCLUSIVE) 
   wTool6=CW_ITTOOLBAR(wToolbar, oUI, 'Manipulators/Annotation', $ 
      /EXCLUSIVE) 
 
   ; Widget Layout 
   ; This section lays out the main portion of the widget  
   ; interface. We create the widget layout in the usual way, 
   ; incorporating iTool compound widgets and "traditional" 
   ; widgets in the desired locations. 
 
   ; Create a base to hold the controls and iTool draw window. 
   wBaseUI = WIDGET_BASE(wBase, /ROW) 
 
   ; Put controls in the left-hand base. 
   wBaseLeft = WIDGET_BASE(wBaseUI, /COLUMN) 
   wButton1 = WIDGET_BUTTON(wBaseLeft, $ 
      VALUE='Insert New Plot', $ 
      EVENT_PRO='draw_plot_event') 
   padBase = WIDGET_BASE(wBaseLeft, YSIZE=5) 
   wSlider = WIDGET_Slider(wBaseLeft, VALUE='10', $ 
      TITLE='Number of points', MINIMUM=5, MAXIMUM=50) 
   padBase = WIDGET_BASE(wBaseLeft, YSIZE=5) 
   wLineSize = WIDGET_DROPLIST(wBaseLeft, $ 
      VALUE=[' 1 ',' 2 ',' 3 ',' 4 '], $ 
      TITLE='Line Size: ', EVENT_PRO='linesize_event') 
   padBase = WIDGET_BASE(wBaseLeft, YSIZE=5) 
   wLineColor = WIDGET_DROPLIST(wBaseLeft, $ 
      VALUE=['Black', 'Red','Green', 'Blue'], $ 
      TITLE='Line Color: ', EVENT_PRO='color_event') 
   padBase = WIDGET_BASE(wBaseLeft, YSIZE=5) 
   wButton2 = WIDGET_BUTTON(wBaseLeft, $ 
      VALUE='Filter this Plot', $ 
      EVENT_PRO='filter_event') 
 
   ; Put the iTool draw window on the right. 
   wBaseRight = WIDGET_BASE(wBaseUI, /COLUMN, /BASE_ALIGN_RIGHT) 
 
   ; Set the initial dimensions of the draw window, in pixels. 
   dimensions = [350, 350] 
 
   ; Create the iTool drawable area. 
   wDraw = CW_ITWINDOW(wBaseRight, oUI, $ 
      DIMENSIONS = dimensions, $ 
      VIRTUAL_DIMENSIONS = virtualDimensions) 
 
   ; Get the geometry of the top-level base widget. 
   baseGeom = WIDGET_INFO(wBase, /GEOMETRY) 
 
   ; Create the status bar. 
   wStatus = CW_ITSTATUSBAR(wBase, oUI, $ 
      XSIZE = baseGeom.xsize-baseGeom.xpad) 
 
   ; If the user did not specify a location, position the 
   ; iTool on the screen. 
   IF (N_ELEMENTS(location) EQ 0) THEN BEGIN 
      location = [(screen[0] - baseGeom.xsize)/2 - 10, $ 
                 ((screen[1] - baseGeom.ysize)/2 - 100) > 10] 
   ENDIF 
 
   WIDGET_CONTROL, wBase, MAP = 0, $ 
      TLB_SET_XOFFSET = location[0], $ 
      TLB_SET_YOFFSET = location[1] 
 
   ; Get the widget ID of the first child widget of our 
   ; base widget. We'll use the child widget's user value 
   ; to store our widget state structure. 
   wChild = WIDGET_INFO(wBase, /CHILD) 
 
   ; Create a state structure for the widget and stash 
   ; a pointer to the structure in the user value of the 
   ; first child widget. 
   state = { $ 
           oTool      : oTool,     $ 
           oUI        : oUI,       $ 
           wBase      : wBase,     $ 
           title      : title,     $ 
           basesize   : [0L, 0L],  $ 
           wToolbar   : wToolbar,  $ 
           wDraw      : wDraw,     $ 
           wStatus    : wStatus,   $ 
           wSlider    : wSlider,   $ 
           wLineSize  : wLineSize, $ 
           wLineColor : wLineColor } 
 
   pState = PTR_NEW(state, /NO_COPY) 
   WIDGET_CONTROL, wChild, SET_UVALUE = pState 
 
   ; Realize our interface. Note that we have left the 
   ; interface unmapped, to avoid flashing. 
   WIDGET_CONTROL, wBase, /REALIZE 
 
   ; Retrieve the starting dimensions and store them. 
   ; Used for window resizing in event processing. 
   WIDGET_CONTROL, wBase, TLB_GET_SIZE = basesize 
   (*pState).basesize = basesize 
 
   ; Register the top-level base widget with the UI object. 
   ; Returns a string containing the identifier of the 
   ; interface widget. 
   myID = oUI->RegisterWidget(wBase, 'Example 2 Tool', $ 
      'example2_wdtool_callback') 
 
   ; Register to receive messages from the iTool components 
   ; included in the interface. 
   oUI->AddOnNotifyObserver, myID, oTool->GetFullIdentifier() 
 
   ; Specify how to handle destruction of the widget interface. 
   WIDGET_CONTROL, wChild, KILL_NOTIFY = "example2_wdtool_cleanup" 
 
   ; Display the iTool widget interface. 
   WIDGET_CONTROL, wBase, /MAP 
 
   ; Start event processing. 
   XMANAGER, 'example2_wdtool', wBase, /NO_BLOCK 
 
END 

Discussion

Most of the important sections of this routine have been discussed in previous sections. There are, however, a few additional points worth noting:

iTool Class Definition Routine Discussion

The class definition routine creates a new iTool class based on the IDLitToolbase class. The Init method simply unregisters operations and manipulators we do not want to appear in the menus and toolbars of our new interface.

Example Code
This iTool class is defined in the file example2tool__define.pro in the examples/doc/itools subdirectory of the IDL distribution. Run the example procedure by entering example2tool__define at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT example2tool__define.pro.

FUNCTION example2tool::Init, _REF_EXTRA = _extra 
 
   ; Call our super class. 
   IF ( self->IDLitToolbase::Init(_EXTRA = _extra) EQ 0) THEN $ 
      RETURN, 0 
 
   ; This tool removes several of the standard iTool operations 
   ; and manipulators. 
   
   ;*** Insert menu 
   self->UnRegister, 'OPERATIONS/INSERT/VISUALIZATION' 
   self->UnRegister, 'OPERATIONS/INSERT/VIEW' 
   self->UnRegister, 'OPERATIONS/INSERT/DATA SPACE' 
   self->UnRegister, 'OPERATIONS/INSERT/COLORBAR' 
 
   ;*** Window menu 
   self->Unregister, 'OPERATIONS/WINDOW/FITTOVIEW' 
   self->Unregister, 'OPERATIONS/WINDOW/DATA MANAGER' 
 
   ;*** Operations menu 
   self->UnRegister, 'OPERATIONS/OPERATIONS/MAP PROJECTION' 
 
   ;*** Toolbars 
   self->UnRegister, 'MANIPULATORS/ROTATE' 
 
   RETURN, 1 
 
END 
 
PRO example2tool__Define 
 
struct = { example2tool,              $ 
           INHERITS IDLitToolbase     $ ; Provides iTool interface 
         } 
 
END 

To find the identifiers of operations and manipulators you wish to unregister, create an instance of the tool with the items still registered, and use the FindIdentifiers method of the IDLitTool class to retrieve the full identifiers of the items you are interested in. See Retrieving Component Identifiers for details.

iTool Launch Routine Discussion

Our iTool launch routine simply registers the example2tool iTool class and the example2_wdtool interface definition, then creates an instance of the Example 2 Tool iTool using the Example2_UI interface.

Example Code
This iTool launch is defined in the file example2tool.pro in the examples/doc/itools subdirectory of the IDL distribution. Run the example procedure by entering example2tool at the IDL command prompt or view the file in an IDL Editor window by entering .EDIT example2tool.pro.

PRO example2tool, IDENTIFIER = identifier, _EXTRA = _extra 
 
   IREGISTER, 'Example 2 Tool', 'example2tool' 
   IREGISTER, 'Example2_UI', 'example2_wdtool', /USER_INTERFACE 
 
   identifier = IDLITSYS_CREATETOOL('Example 2 Tool',$ 
      VISUALIZATION_TYPE = ['Plot'], $ 
      USER_INTERFACE='Example2_UI', $ 
      TITLE = 'Example iTool Interface', $ 
      _EXTRA = _extra) 
END 

Note that our launch routine does not allow the iTool to accept command-line arguments. A more sophisticated iTool might allow the user to supply data at the command line, as described in Creating an iTool Launch Routine.