Creating a New Visualization Type
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:
Creating the Class Structure Definition
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 (Object Programming).
Note
The class structure definition is generally the last routine in the .pro file that defines an object class.
Subclassing from the IDLitVisualization 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" (IDL Reference Guide) for details on the methods and properties available to classes that subclass from IDLitVisualization.
Hiding Compilation Messages
When IDL compiles an object class, it prints a compilation message similar to the following to the IDL Console:
To prevent the compilation message from appearing when the class is compiled, add the following line to the class structure definition:
Example Class Structure Definition
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 COMPILE_OPT hidden struct = { ExampleVis, $ INHERITS IDLitVisualization, $ _oPlot: OBJ_NEW(), $ _oSymbol: OBJ_NEW(), $ _exampleProperty: '' $ } END
Discussion
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.
Creating an Init Method
The visualization class Init method handles any initialization required by the visualization object, and should do the following:
- define the Init function method, using the keyword inheritance mechanism to handle "extra" keywords
- call the Init methods of any superclasses, using the keyword inheritance mechanism to pass "extra" keywords
- register any data parameters used when creating visualizations of the new type
- register any properties of your visualization type, and set property attributes as necessary
- create all the graphics objects needed by the visualization, and add them to the visualization object
- define a custom selection visual, if desired
- perform other initialization steps as necessary
- return the value 1 if the initialization steps are successful, or 0 otherwise
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.
Definition of the Init Function
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 (Application Programming) for details on IDL's keyword inheritance mechanism.
Superclass Initialization
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:
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.
Error Checking
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:
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.
Keywords to the Init Method
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" (IDL Reference Guide).
Note
Always use keyword inheritance (the _EXTRA keyword) to pass keyword parameters through to the superclass. See Keyword Inheritance (Application Programming) for details on IDL's keyword inheritance mechanism.
Standard Base Class
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:
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.
Return Value
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.
Registering Parameters
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:
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.
Registering Properties
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:
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.
Property Aggregation
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:
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, /AGGREGATE
is the same as the following two calls: self->Add, Object_Reference
self->Aggregate, Object_Reference
Setting Property Attributes
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:
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.
Adding Graphics Objects to the Visualization
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:
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:
- The visualization or graphics object reference should generally be placed in a specific field of the visualization type's class structure. This allows you access to the object when you have the reference to the visualization object itself.
- In most cases, you will want to include the REGISTER_PROPERTIES keyword in the call to OBJ_NEW when creating a visualization or graphics object instance. This keyword does the work of registering all registrable properties of the object automatically, relieving you from the need to manually register the properties you want to show up in the visualization's property sheet.
- Including the PRIVATE keyword in the call to OBJ_NEW indicates that the visualization or graphics object should not appear in the iTools visualization browser itself; users gain access to the object's properties via the visualization to which the object is being added.
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.
Passing Through Caller-Supplied Property Settings
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:
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.
Example Init Method
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
Discussion
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:
- Calls the Init method of the superclass, IDLitVisualization. We use the REGISTER_PROPERTIES keyword to ensure that all registrable properties of the superclass are exposed in the ExampleVis object's property sheet. We also set the visualization type to be an "ExampleVis," provide a Name for the object instance, and provide an icon. Finally, we use the _EXTRA keyword inheritance mechanism to pass through any keywords provided when the
ExampleVisInit method is called. - Registers an input parameter called Y that must be a vector. The OPTARGET keyword specifies that the Y parameter can be the target for iTool operations.
- Creates a plotting symbol created from the IDLitSymbol class and aggregate its properties with the other ExampleVis properties.
- Creates an IDLitGrPlot object that uses the IDLitSymbol object for its plotting symbols.
- Registers an example property that holds a string value.
- Passes any "extra" keyword properties through to the SetProperty method.
- Returns the integer 1, indicating successful initialization.
Creating a Cleanup Method
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" (IDL Reference Guide) for additional details.
Example Cleanup Method
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
Discussion
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.
Creating a GetProperty Method
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" (IDL Reference Guide) for additional details.
Example GetProperty Method
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
Discussion
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.
Creating a SetProperty Method
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" (IDL Reference Guide) for additional details.
Example SetProperty Method
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
Discussion
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.
Creating an OnDataChangeUpdate Method
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" (IDL Reference Guide) and Data Update Mechanism for additional details.
Example OnDataChangeUpdate Method
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
Discussion
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.
Creating an OnDataDisconnect Method
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" (IDL Reference Guide) for additional details.
Example OnDataDisconnect Method
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
Discussion
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.