The ability to extend the itools is one of their main strengths. This can be adding a button for an operation (like our example here), the capability to read/write new file types, new visualizations, or new ways to manipulate visualizations. Plus, the user interface can be customized to various levels. Toolbars and panels can be added to the standard configuration, but a totally custom interface can be created also. Capabilities can also be removed to provide a simpler interface for a specific task.

Screenshot of using MGitOpAdaptHistEqual

This example adds an adaptive histogram equalization operation to IIMAGE.

There are two files for this demo program: (doc) to define the operation and (doc) to setup using the operation. To run the demo, make sure both files are compiled or in your path and run MG_DATAOP_DEMO.

You’ll probably want to look at the documentation for the parent classes of our class: IDLitComponent, IDLitIMessaging, IDLitOperation, and IDLitDataOperation during the course of this tutorial.

Define the member variables

The member variables will include all the properties of the operation, plus whatever else is needed to be stored during the life of the operation. The properties will be registered in the init method using the names defined here.

The init and cleanup methods

The init method has several jobs:

  1. call IDLitDataOperation::init with the proper keywords (TYPES and _EXTRA),
  2. register properties, and
  3. initialize member variables (properties).

As usual for a subclass’ init method, the first task is to call the parent’s init method:

if (~self->idlitdataoperation::init(types=['IDLARRAY2D'], $
                                    name='Adapt Hist Eq', $
                                    icon='image', $
                                    reversible_operation=0B, $
                                    expensive_operation=0B, $
                                    _extra=e)) then return, 0L

Note that it is important that it get passed _EXTRA. The TYPES determines which itools data types the operation will act on. The REVERSIBLE_OPERATION and EXPENSIVE_OPERATION keywords relate to the undo/redo system. REVERSIBLE_OPERATION determines what happens when the user undo’s the operation. If REVERSIBLE_OPERATION is set, then the undoExecute method is called. If REVERSIBLE_OPERATION is not set, then the itools will cache data before the operation is executed so that it can be restored when it is undone. EXPENSIVE_OPERATION determines the behavoir after the operation is undone. If EXPENSIVE_OPERATION is set, then the result of the operation is cached so it can be redone without having to calculate it again. If not set it must be calculated again via the execute method if it is redone.

self->registerProperty, 'clip', /float, $
                        description='Slope limit of histogram', $
                        name='Clip', $

Register properties of the operation via IDLitComponent::registerProperty (IDLitComponent is a parent class of IDLitDataOperation). The positional parameter is the member variable name. The type is specified using the INTEGER and FLOAT keywords (others are available, though not corresponding to all the IDL types), but could be specifed via a code as the second positional parameter.

The cleanup method simply calls IDLitDataOperation::cleanup.

The setProperty and getProperty methods

The setProperty and getProperty methods are fairly standard. They must provide access to all registered properties and must pass keywords on to IDLitDataOperation::setProperty and IDLitDataOperation::getProperty.

The doExecuteUI, execute, and undoExecute methods

The doExecuteUI method is called to present a GUI for the user to modify properties, see a preview, etc. before the operation is executed. It gets the tool object reference, does a quick sanity check, and does a UI service:

otool = self->getTool()
if (~otool) then return, 0L
return, otool->doUIService('OperationPreview', self)

Documented predefined choices for services are: ‘PropertySheet’ or ‘OperationPreview’. You can also write your own UI service. Note that this method will not be called if the SHOW_EXECUTION_UI property of the operation is not set.

The execute method actually does the calculations of the operation. The function accepts a single positional parameter which is an input for the original data and an output for the operated upon data. This is the statement that actually does the adaptive histogram equalization:

data = adapt_hist_equal(bytscl(data), $
                        clip=self.clip, $
                        nregions=self.nregions, $

The status of the execution is returned (1 for success, 0 for failure).

The undoExecute method is not needed for this operation because REVERSIBLE_OPERATION is not set.

Using the operation

MG_DATAOP_DEMO puts MGitOpAdaptHistEqual into IIMAGE and loads some default data. The itool is created with a data set in the standard way:

f = filepath('endocell.jpg', subdir=['examples', 'data'])
endo = read_image(f)
iimage, endo
id = itGetCurrent(tool=otool)

Here, ITGETCURRENT gets the itool identifier and object reference. We only need the object reference, but are required to get the identifier. Be sure to use ITGETCURRENT right after creating the itool, because if another itool is created or even becames the current window in the OS, it will be the current itool.

otool->registerOperation, 'AdaptHistEqual', $
                          'MGitOpAdaptHistEqual', $
                          identifier='Operations/AdaptHistEqual', $

This registers the operation on our itool. The positional parameters give a name for our operation and the classname that defines the operation. The IDENTIFIER keyword locates the operation in the menu system. For instance, the identifier ‘File/AdaptHistEqual’ would put this in the File menu of the itool. More usefully, ‘Operations/My Operations/AdaptHistEqual’ would create a new submenu in the Operations menu and put this operation in it. The ICON keyword specifies an icon for the operation in the operation browser. In this case, icon='image' is equivalent to

icon = filepath('image.bmp', subdir=['examples', 'data'])

Hopefully, this will provide a template for further operations. I hope to provide more demos/templates/tutorials for other methods of extending the itools including file readers, file writers, general operations, manipulators, visualizations, UI services, user interface panels, and full-blown user interfaces.