|
iTool Programming: Creating a Visualization |
|
An iTool visualization class definition file must (at the least) provide methods to initialize the visualization class, get and set property values, handle changes to the underlying data, clean up when the visualization is destroyed, and define the visualization class structure. Complex visualization types will likely provide additional methods.
The process of creating a visualization type is outlined in the following sections:
When any IDL object is created, IDL looks for an IDL class structure definition that specifies the instance data fields needed by an instance of the object, along with the data types of those fields. The object class structure must have been defined before any objects of the type are created. In practice, when the IDL OBJ_NEW function attempts to create an instance of a specified object class, it executes a procedure named ObjectClass__define (where ObjectClass is the name of the object), which is expected to define an IDL structure variable with the correct name and structure fields. For additional information on how IDL creates object instances, see The Object Lifecycle.
| Note The class structure definition is generally the last routine in the .pro file that defines an object class. |
The IDLitVisualization class serves as a container for visualization objects displayed in an iTool. The class includes methods to handle changes to data and property values automatically; in almost all cases, new visualization types will be subclassed from the IDLitVisualization class. See IDLitVisualization for details on the methods and properties available to classes that subclass from IDLitVisualization.
The following is the class structure definition for the ExampleVis visualization class. This procedure should be the last procedure in a file named examplevis__define.pro.
PRO ExampleVis__Define
struct = { ExampleVis, $
INHERITS IDLitVisualization, $
_oPlot: OBJ_NEW(), $
_oSymbol: OBJ_NEW(), $
_exampleProperty: '' $
}
END
The purpose of the structure definition routine is to define a named IDL structure with structure fields that will contain the visualization object instance data. The structure name should be the same as the visualization's class name — in this case, ExampleVis.
Like many iTool visualizations, ExampleVis is created as a subclass of the IDLitVisualization class. Visualization classes that subclass from the IDLitVisualization class inherit all of the standard iTool visualization features, as described in Subclassing from the IDLitVisualization Class.
The ExampleVis visualization class instance data includes two graphics objects: an IDLitVisPlot object, to which a reference is stored in the _oPlot class structure field, and an IDLitSymbol object, to which a reference is stored in the _oSymbol class structure field. Both graphics objects are defined in the class structure definitions as object instances, denoted by the presence of the OBJ_NEW() after the structure field name. Finally, instance data for a string property named ExampleProperty is stored in the _exampleProperty class structure field.
| Note This example is intended to demonstrate how simple it can be to create a new visualization class definition. While the class definition for a visualization class with significant extra functionality will likely define additional structure fields, and may inherit from other iTool classes, the basic principles are the same. |
The visualization class Init method handles any initialization required by the visualization object, and should do the following:
| Note While the Init method registers data parameters for a visualization, it does not accept data parameters itself. Data parameters are set in the OnDataChangeUpdate method. |
Begin by defining the argument and keyword list for your Init method. The argument and keyword list defines positional parameters (arguments) accepted by your method, defines any keywords that will be handled directly by your method, and specifies whether keywords not explicitly handled by your method will be passed through to other routines called by your method via IDL's keyword inheritance mechanism. The Init method for a visualization type generally looks something like this:
FUNCTION MyVisualization::Init, MYKEYWORD1 = mykeyword1, $ MYKEYWORD2 = mykeyword2, ..., _REF_EXTRA = _extra
where MyVisualization is the name of your visualization class and the MYKEYWORD parameters are keywords handled explicitly by your Init function.
Always use keyword inheritance (the _REF_EXTRA keyword) to pass keyword parameters through to any called routines. See Keyword Inheritance for details on IDL's keyword inheritance mechanism.
The visualization class Init method should call the Init method of any required superclass. For example, if your visualization class is based on an existing visualization, you would call that visualization's Init method:
success = self->SomeVisualizationClass::Init(_EXTRA = _extra)
where SomeVisualizationClass is the class definition file for the visualization on which your new visualization is based. The variable success will contain a 1 if the initialization is successful.
| Note Your visualization class may have multiple superclasses. In general, each superclass' Init method should be invoked by your class' Init method. |
Rather than simply calling the superclass Init method, it is a good idea to check whether the call to the superclass Init method succeeded. The following statement checks the value returned by the superclass Init method; if the returned value is 0 (indicating failure), the current Init method also immediately returns with a value of 0:
IF (self->SomeVisualizationClass::Init(_EXTRA = _extra) EQ 0) THEN $ RETURN, 0
This convention is used in all visualization classes included with IDL. We strongly suggest that you include similar checks in your own class definition files.
Properties of the visualization type class can be set in the Init method by specifying the property names and values as IDL keyword-value pairs. In addition to any keywords implemented directly in the Init method of the superclass on which you base your class, the properties of the IDLitVisualization class are available to any visualization class. See IDLitVisualization Properties.
| Note Always use keyword inheritance (the _EXTRA keyword) to pass keyword parameters through to the superclass. See Keyword Inheritance for details on IDL's keyword inheritance mechanism. |
While you can create your new visualization class from any existing visualization class, in many cases, visualization classes you create will be subclassed directly from the base class IDLitVisualization:
IF (self->IDLitVisualization::Init(_EXTRA = _extra) EQ 0) $ THEN RETURN, 0
The IDLitVisualization class provides the base iTool functionality used in the visualization classes created by ITT Visual Information Solutions. See Subclassing from the IDLitVisualization Class for details.
If all of the routines and methods used in the Init method execute successfully, the method should indicate successful initialization by returning 1. Other visualization classes that subclass from your visualization class may check this return value, as your routine should check the value returned by any superclass Init methods called.
Visualization types must register each data parameter used to create the visualization. Data parameters are described in detail in Data Management.
Register a parameter by calling the RegisterParameter method of the IDLitParameter class:
self->RegisterParameter, ParmameterName, $ TYPES = ['DataType1', ..., 'DataTypeN']
where ParameterName is a string that defines the name of the parameter and the TYPES keyword is set equal to a string or array of strings specifying the iTool system data types the parameter can represent. See Registering Parameters for additional details.
Visualization types can register properties with the iTool. Registered properties show up in the property sheet interface, and can be modified interactively by users. The iTool property interface is described in detail in Property Management.
Register a property by calling the RegisterProperty method of the IDLitComponent class:
self->RegisterProperty, PropertyIdentifier [, TypeCode] $ [, ATTRIBUTE = value]
where PropertyIdentifier is a string that uniquely identifies the property, TypeCode is an integer between 0 and 9 specifying the property data type, and ATTRIBUTE is a property attribute. See Registering Properties for details.
IDL objects can contain other objects; a visualization type is, at one level, simply an object container that holds the different graphics objects that make up a visualization. The iTools property aggregation mechanism allows the properties of several different objects held by the same container object to be displayed in the same property sheet automatically. Without property aggregation, you would have to manually register all of the properties of the objects contained in your visualization type object.
Aggregate the properties of contained objects using the Aggregate method of the IDLitVisualization class:
self->Aggregate, Object_Reference
where Object_Reference is a reference to the object whose properties you want aggregated into the visualization object. See Property Aggregation for additional details.
| Note The IDLitVisualization::Add method includes an AGGREGATE keyword. This keyword is simply a shorthand method of aggregating the properties of an object during the call to the Add method, eliminating the need to call the Aggregate method separately. The call self->Add, Object_Reference, /AGGREGATEis the same as the following two calls: self->Add, Object_Reference |
If a property has already been registered, perhaps by a superclass of your visualization class, you can change the registered attribute values using the SetPropertyAttribute method of the IDLitComponent class:
self->SetPropertyAttribute, Identifier
where Identifier is the name of the keyword to the GetProperty and SetProperty methods used to retrieve or change the value of this property. (The Identifier is specified in the call to RegisterProperty either via the PropertyName argument or the IDENTIFIER keyword.) See Property Attributes for additional details.
An iTool visualization type must contain at least one IDLit* visualization object or IDLgr* graphics object. To add a visualization or graphics object, you must first create an instance of the object using the OBJ_NEW function, then add the object instance to the visualization using the Add method of the IDLitVisualization class:
Graphics_Object = OBJ_NEW('IDLitVisObject')
self->Add, Graphics_Object
where IDLitVisObject is an actual IDL iTool visualization class, such as IDLitVisPlot.
In practice, you should also consider the following when adding a visualization or graphics object to a visualization type:
A typical addition of a graphics object to a visualization looks like this:
self._oPlot = OBJ_NEW('IDLitVisPlot', /REGISTER_PROPERTIES, $
/PRIVATE)
self->Add, self._oPlot, /AGGREGATE
Here, we create a new IDLitVisPlot object instance and place the object reference in the _oPlot field of the visualization's class structure. The REGISTER_PROPERTIES keyword ensures that all of the registrable IDLitVisPlot properties are registered with the visualization automatically. Next, we use the Add method to add the object instance to our visualization; this inserts the object into the visualization's graphics hierarchy. Finally, we use the AGGREGATE keyword to include all of the IDLitVisPlot object's registered properties in the visualization's property sheet.
If you have included the _REF_EXTRA keyword in your function definition, you can use IDL's keyword inheritance mechanism to pass any "extra" keyword values included in the call to the Init method through to other routines. One of the things this allows you to do is specify property settings when the Init method is called; simply include each property's keyword/value pair when calling the Init method, and include the following in the body of the Init method:
IF (N_ELEMENTS(_extra) GT 0) THEN $ self->MyVisualization::SetProperty, _EXTRA = _extra
where MyVisualization is the name of your visualization class. This line has the effect of passing any "extra" keyword values to your visualization class' SetProperty method, where the keyword can either be handled directly or passed through to the SetProperty methods of the superclasses of your class. See Creating a SetProperty Method for details.
The following example code shows a very simple Init method for a visualization type named ExampleVis. This function would be included (along with the class structure definition routine and any other methods defined by the class) in a file named examplevis__define.pro.
FUNCTION ExampleVis::Init, _REF_EXTRA = _extra
; Initialize the superclass.
IF (self->IDLitVisualization::Init(/REGISTER_PROPERTIES, $
TYPE='ExampleVis', NAME='Example Visualization Type', $
ICON='plot', /PRIVATE, _EXTRA = _extra) NE 1) THEN $
RETURN, 0
; Register a parameter
self->RegisterParameter, 'Y', DESCRIPTION='Y Plot Data', $
/INPUT, TYPES='IDLVECTOR', /OPTARGET
; Add a plotting symbol object and aggregate its properties
; into the visualization.
self._oSymbol = OBJ_NEW('IDLitSymbol', PARENT = self)
self->Aggregate, self._oSymbol
; Create an IDLitVisPlot object, setting its SYMBOL property to
; the symbol object we just created. Add the plot object to the
; visualization, and aggregate its properties.
self._oPlot = OBJ_NEW('IDLitVisPlot', /REGISTER_PROPERTIES, $
SYMBOL = self._oSymbol->GetSymbol())
self->Add, self._oPlot, /AGGREGATE
; Register an example property that holds a string value.
self->RegisterProperty, 'ExampleProperty', $
/STRING, DESCRIPTION='An example property', $
NAME='Example Property', SENSITIVE = 1
; Pass any extra keyword parameters through to the SetProperty
; method.
IF (N_ELEMENTS(_extra) GT 0) THEN $
self->ExampleVis::SetProperty, _EXTRA = _extra
; Return success
RETURN, 1
END
The ExampleVis class is based on the IDLitVisualization class (discussed in Subclassing from the IDLitVisualization Class). As a result, all of the standard features of an iTool visualization class are already present. We don't define any keyword values to be handled explicitly in the Init method, but we do use the keyword inheritance mechanism to pass keyword values through to methods called within the Init method. The ExampleVis Init method does the following things:
ExampleVis Init method is called.
The visualization class Cleanup method handles any cleanup required by the visualization object, and should do the following:
Calling the superclass' cleanup method will destroy any objects that were added to the graphics hierarchy.
See IDLitVisualization::Cleanup for additional details.
The following example code shows a very simple Cleanup method for the ExampleVis visualization type:
PRO ExampleVis::Cleanup ; Clean up the IDLitSymbol object we created. OBJ_DESTROY, self._oSymbol ; Call superclass Cleanup method self->IDLitVisualization::Cleanup END
The Cleanup method first destroys the IDLitSymbol object, which is not part of the graphics hierarchy, then calls the superclass Cleanup method to destroy the objects in the graphics hierarchy.
The visualization class GetProperty method retrieves property values from the visualization object instance or from instance data of other associated objects. The method can retrieve the requested property value from the visualization object's instance data or by calling another class' GetProperty method.
| Note Any property registered with a call to the RegisterProperty method must be listed as a keyword to the GetProperty method either of the visualization class or one of its superclasses. |
See IDLitVisualization::GetProperty for additional details.
The following example code shows a very simple GetProperty method for the ExampleVis visualization type:
PRO ExampleVis::GetProperty, $ EXAMPLEPROPERTY = exampleProperty, $ _REF_EXTRA = _extra IF ARG_PRESENT(exampleProperty) THEN BEGIN exampleProperty = self._exampleproperty ENDIF ; get superclass properties IF (N_ELEMENTS(_extra) GT 0) THEN $ self->IDLitVisualization::GetProperty, _EXTRA = _extra END
The GetProperty method first defines the keywords it will accept. There must be a keyword for each property of the visualization type. The keyword inheritance mechanism allows properties to be retrieved from the ExampleVis class' superclasses without knowing the names of the properties.
Using the ARG_PRESENT function, the method checks for the presence of keywords in the call to the GetProperty method. If a keyword is detected, it retrieves the value of the associated property from the object's instance data. In this example, only one property (ExampleProperty) is specific to the ExampleVis object.
Finally, the method calls the superclass' GetProperty method, passing in all of the keywords stored in the _extra structure.
The visualization class SetProperty method stores property values in the visualization object's instance data or in properties of associated objects. It sets the specified property value either by storing the value directly in the visualization object's instance data or by calling another class' SetProperty method.
| Note Any property registered with a call to the RegisterProperty method must be listed as a keyword to the SetProperty method either of the visualization class or one of its superclasses. |
See IDLitVisualization::SetProperty for additional details.
The following example code shows a very simple SetProperty method for the ExampleVis visualization type:
PRO ExampleVis::SetProperty, $ EXAMPLEPROPERTY = exampleProperty, $ _REF_EXTRA = _extra IF (N_ELEMENTS(exampleProperty) GT 0) THEN BEGIN self._exampleProperty = exampleProperty ENDIF IF (N_ELEMENTS(_extra) GT 0) THEN $ self->IDLitVisualization::SetProperty, _EXTRA = _extra END
The SetProperty method first defines the keywords it will accept. There must be a keyword for each property of the visualization type. The keyword inheritance mechanism allows properties to be set on the ExampleVis class' superclasses without knowing the names of the properties.
Using the N_ELEMENTS function, we check to see whether a value was specified for each keyword. If a value is detected, we set the value of the associated property. In this example, only one property (ExampleProperty) is specific to the ExampleVis object. We set the value of the ExampleProperty directly in the ExampleVis object's instance data.
Finally, we call the superclass' SetProperty method, passing in all of the keywords stored in the _extra structure.
The visualization class OnDataChangeUpdate method takes care of updating the visualization when one or more of the data parameters used to create the visualization change their values. The tasks this method must perform are dependent on the type of visualization involved and the data parameter that changes. The general idea is that when the value of a data object changes, the OnDataChangeUpdate method for each visualization that uses that data is called. The OnDataChangeUpdate method then uses the GetData method to retrieve the changed data from the IDLitData object, inspects the data and manipulates it as necessary, and uses the SetProperty method to insert the new data values into the visualization object.
See IDLitParameter::OnDataChangeUpdate and Data Update Mechanism for additional details.
The following example code shows a very simple OnDataChangeUpdate method for the ExampleVis visualization type:
PRO ExampleVis::OnDataChangeUpdate, oSubject, parmName CASE STRUPCASE(parmName) OF '<PARAMETER SET>': BEGIN oParams = oSubject->Get(/ALL, COUNT = nParam, $ NAME = paramNames) FOR i = 0, nParam-1 DO BEGIN IF (paramNames[i] EQ '') THEN CONTINUE oData = oSubject->GetByName(paramNames[i]) IF (OBJ_VALID(oData)) THEN $ self->OnDataChangeUpdate, oData, paramNames[i] ENDFOR END 'Y': BEGIN success = oSubject->GetData(data) nData = N_ELEMENTS(data) IF (nData GT 0) THEN BEGIN ; Set the min/max values. minn = MIN(data, MAX = maxx) self._oPlot->SetProperty, DATAY = TEMPORARY(data), $ MIN_VALUE = minn, MAX_VALUE = maxx ENDIF END ELSE: self->ErrorMessage, 'Unknown parameter' ENDCASE END
The OnDataChangeUpdate method must accept two arguments: an object reference to the data object whose data has changed (oSubject in the previous example), and a string containing the name of the parameter associated with the data object (parmName in the example).
| Note The string <PARAMETER SET> is a special case value for the second argument, used to indicate that the object reference is not a single data object but a parameter set. Calling OnDataChangeUpdate with a parameter set rather than a data item provides a simple way to update a group of data values with a single statement; this can be very useful when creating the visualization for the first time. |
We use a CASE statement to determine which parameter has been modified, and process the data as appropriate. We first handle the special case where the parameter has the value <PARAMETER SET> by looping through all of the parameters in the parameter set object, calling the OnDataChangeUpdate method again on each parameter.
Next, we handle the parameter (Y) by calling the IDLitData::GetData method on the data object reference stored in the oSubject argument. We use the N_ELEMENTS function to determine whether any data was returned. If data was returned, we determine the minimum and maximum values. Finally, we use the SetProperty method to insert the changed data (using the TEMPORARY function to avoid making a copy of the data) into the DATAY property of the IDLitVisPlot object stored in the visualization's _oPlot class structure field. Similarly, we insert the new minimum and maximum values into the MIN_VALUE and MAX_VALUE properties of the IDLitVisPlot object.
The visualization class OnDataDisconnect method is called automatically when a data value has been disconnected from a parameter. A visualization class based on the IDLitVisualization class must implement this method in order for changes or additions to the data parameters to be updated automatically in the resulting visualizations. The general idea is that when a data item is disassociated from a visualization parameter, one or more properties of the visualization may need to be reset to reasonable default values. For example, in the case of a plot visualization, if the plotted data is disconnected, we want to reset the data ranges to their default values and hide the plot visualization.
See IDLitParameter::OnDataDisconnect for additional details.
PRO ExampleVis::OnDataDisconnect, ParmName CASE ParmName OF 'Y': BEGIN self._oPlot->SetProperty, DATAX = [0,1], DATAY = [0,1] self._oPlot->SetProperty, /HIDE END ELSE: ENDCASE END
The OnDataDisconnect method takes a single argument, which contains the upper-case name of the parameter that was disconnected. In the case of our ExampleVis visualization, we only need to handle the Y parameter. If the Y parameter is disconnected, we set the data ranges of the plot object to their default values (the range between 0 and 1), and hide the plot visualization using the HIDE property.
IDL Online Help (March 06, 2007)