Tags:
create new tag
view all tags

PDF Version Portrait Landscape

Help Did you spot something wrong or missing on this page? If you have an account on this TWiki, you can fix it yourself by editing the page. If you don't have an account, you can leave a message at the bottom of the page to tell us about it. Thank you in advance!

Adding tools to HIPE

Task, tools and variables

Tools are processing units that operate on specific data elements.
From the Java point of view, a tool is an implementation of the Tool interface.
Tasks are examples of tools within HIPE. In this case, TaskTool is used under the hood. See the topic on WritingTasks for details.

If a data element is selected, a list of tools that can operate on that element should appear. Double clicking on the tool will open an associated view for settings parameters.

This section explains the following:

  • How you can create a tool and register it, so that it becomes associated to specific data.
  • How you can make HIPE aware of an existing Task.
  • How your task can react better to an active data element.
  • The default task dialogue window.
  • How you can implement and contribute a specific parameter editor.
  • How to deprecate tasks and task parameters.


Adding a task as a HIPE tool

Registering tasks

You can make your task globally available to the system by specifying an instance of that task within a Jython file (usually called __init__.py for HIPE internal modules) that is read by HIPE at startup (see My first Java HIPE task). For example:
    # __init__.py file
    compute = ComputeTask()

This is fine if you just want to load your task. To make your task appear classified in the Tasks view (and to allow it to be Applicable when some data is selected), you should create and register your task in the same step as follows (the system will create the task variable for you, with the proper name Task.getName()):

 // Jython 
    from herschel.ia.task.views import TaskToolRegistry
    toolRegistry = TaskToolRegistry.getInstance()
    toolRegistry.register(ComputeTask())

You can also specify that your task belongs to one or more Category (unless said otherwise registered tasks belongs to the GENERAL category) :

 // Jython 
    from herschel.ia.gui.kernel.Tool import Category
    toolRegistry.register(ComputeTask(), [Category.IMAGE, Category.PACS]))

To select the prime input parameter, modify your task constructor as follows:

 // Java 
    class ComputeTask extends Task {
       ComputeTask() {
          super("compute");
          prime = new TaskParameter("spectrum", SpecificProduct.class);
          addTaskParameter(prime);
          getSignature().setPrimeInput(prime);
       }
    }
Otherwise the first (added) input parameter of your task will be its prime input. The prime input is the one that "decides" if your task can be applied to some variable.

For your task to appear in the Applicable category whenever the user selects a suitable variable, you still need to define a Validator. See Validating prime input below.

Warning, important Tasks without any input cannot be registered and should not be used (create a parameterless function instead).

Tasks Naming Conventions

Tasks registered in HIPE should follow the naming conventions shown in this example:

  • Name of the class: ReduceTask (Capitalized task name with Task suffix)
  • Name of the task (given by getName()): reduce
  • Name of the task variable in Jython: reduce (created automatically when registered)

Warning, important For naming tasks follow the Java conventions, that is, use camelCase and avoid underscores: reduceLight is valid, reduce_light is not. HIPE displays info log messages for invalid task names. Note that task names are used as variable names (of type Task) automatically created when starting HIPE.

Validating prime input

In most cases, the type of the prime input is not enough to determine whether a task is applicable to a variable. Your task may only run on a SpecificProduct if it has certain contents: a typical example is a SPIRE task operating on an ObservationContext: clearly, such task should not be listed when a HIFI observation is selected.

This is why you must write a ParameterValidator (extend ParameterValidatorAdapter) to restrict a task to certain product contents:

 // Java 
    prime = new TaskParameter("spectrum", SpecificProduct.class)
    prime.setParameterValidator(new ParameterValidatorAdapter() {
        public void validate(Object value) throws ParameterValidationException {
            SpecificProduct s = (SpecificProduct)value;
            if (! ( _[logic that would validate just the contents of the value and does not use the file system or network]_ )) {
                throw new ParameterValidationException( _[reason]_ );
            }
        }
    });
In Jython, your can define your own ParameterValidatorAdapter classes, although we suggest that you use Java validator classes:

# Jython
from herschel.ia.gui.kernel import ParameterValidatorAdapter, ParameterValidationException

class MyVal(ParameterValidatorAdapter):
    def validate(self, val):
        if (val < 0 or val > 10):
            raise ParameterValidationException("Bad value " + str(val) + ", must be between 0 and 10")

And you can assign instances to your parameters

# Jython
 p.parameterValidator = MyVal()

Also, a prebuilt confirming (always returns true) Validator is available in TaskParameter :

 // Java 
parameter.setParameterValidator(TaskParameter.TRUE_VALIDATOR);

Note how the validation logic is now within the parameter validation block, rather than in the preamble or execution block of your task. One advantage is that the execution block of your task can concentrate on the core algorithm.

A validator is mandatory for the task to appear in the Applicable category of the Tasks view. If your task really is very general and applies to a given variable type with no exceptions, use TaskParameter.TRUE_VALIDATOR. There are also support classes for writing common validators: AnyOfValidator, InstanceOfValidator, PredicateValidator, RangeValidator, RegexValidator in package ia.gui.apps.validator. Please, do not implement ParameterValidator interface directly as it will block extension of it: use ParameterValidatorAdapter (that also detects some validation problems). Note that your validators must be fast, self-contained (with no visible side effects) and not use I/O : they are called frequently and from multiple threads, including the EDT in the case of GUI validation.

Avoiding Applicability

You can veto appearing in Applicable in Tasks View by overriding Task.canBeApplicable() to return false. This applies, for example, to tasks whose primary input is a number or a String (and they have/need validators).

Task dialogue windows

HIPE generates a default input dialog for all registered tasks (note that they are just views, they are called dialogs for historical reasons). The dialog has three sections: Input, Output and Info, each of which can be collapsed. The following two figures show the interface of the crop task, with extended and collapsed sections:

crop_closed.png

crop_new.png

The first section (Inputs) is implemented with a SignatureComponent. Parameters are listed in two columns. HIPE lists the parameters from left to right and top to bottom. If a single input or output parameter is present or if its gui is detected as "big" (for example a JFilePathModifier), it fills a whole line.

If you want to organise your parameters by grouping them in tabs, you can define groups. See Task.getGroups() Below, a task with two parameter groups (tabs):

Group 1 of asciiTableReader

Group 2 of asciiTableReader

Sample code in Java and Jython:

 // Jython 
    from herschel.ia.task import ParameterGroups, Group
    def getGroups(self):
        return ParameterGroups(self, [
           Group("Pool", "Export a pool", ["input"]), 
           Group("Observations", "Export Observations to HSA Directory format", ["input2"])])

 // Java 
    @Override
    public ParameterGroups getGroups() {
        Group g1 = new Group("Basic", "Basic interface for simple table data", FILE, TABLE_TYPE,
                PARSER_SKIP);
        Group g2 = new Group("Advanced", "Comprehensive interface for complex table data",
                ADVANCED_NAMES);
        return new ParameterGroups(this, g1, g2);
    }

This automatic layout may not suit your task for several reasons:

  • A parameter may need several input fields (see for instance the angle parameter of the rotate task, in the figure below).
  • You may only want to provide a sub-set of parameters (and leave the full set to expert users on the command line).
  • You may want to organise your parameters by putting a border around a group of parameters.
  • You may want to have more informative tooltips, labels or even input fields that are more suitable to your task.

Below, a hand-made TaskSignatureComponent (herschel.ia.task.example.RotateTaskSignatureComponent)

rotate_new.png

The following sections explore three ways to customise your task dialogue windows:

  • Parameter modifiers.
  • Signature components.
  • Custom task panels.

Parameter modifiers

In the default TaskSignatureComponent (JTaskSignatureComponent), each input parameter of a task corresponds to a pair of label and Modifier shown in two columns. HIPE adds Modifier objects according to the types of the input parameters in the task signature.

The non masked component shows a JTaskSignatureComponent. The crop task displays five input parameters: image, row1, column1, row2, column2:

crop_input_new.png

At the left hand side of each modifier there is a kind of label with the name of each parameter (TaskUtil.getDecoratedLabel) that also has contextual (right mouse button) commands. HIPE provides modifiers for basic types Boolean, Integer, Float, Long, Double, String and few more, so there is still a lot of room for improvements and contributions. You can find the general available modifiers in the herschel.ia.gui.apps.modifier package; please consult the Javadoc of your HIPE installation.

If no specific modifier fits your task parameter, the default modifier will be used. The default modifier only has a dot to drop variables. Others modifiers include an editor at the right of the dot to enter values. You can implement a custom Modifier and register it in the system to be used whenever a parameter of the registered type is used. You can also write your specific Modifier for any of the already registered types. In that case, you can create and assign your modifier (and listeners...) to your parameter in Task.getCustomModifiers() see Registering a modifier.

Warning, important NOTE:
The following behaviours and limitations are present in the provided modifiers:

  • Not all modifiers are registered: for example if your parameter is of type String but will represent a file (path) the system will choose just a JStringModifier, so you will have to explicitly associate your parameter (name) to a JFilePathModifier in Task.getCustomModifiers().
  • Also, if you want to tweak a (registered) modifier or add listeners between them you will also have to use Task.getCustomModifiers(). Examples of this include using a specific constructor or update modifiers when a linked modifier is changed.
  • While you can always write SomeTask(param = null) on the command line, using a task dialog you will just get SomeTask(): modifiers use null to signify that they have no value and task GUI command generation interprets this as "not using this parameter".
  • Modifiers have no notion of the optionality of parameters (or default values): if they have a valid value, they will return it. Task command generation for GUIs will not generate a parameter assignment if the value equals the default. See Task Preferences to change this default behaviour.
  • Modifiers will mark as erroneous any variables incompatible with the type (dynamically) and validator, but will neither reject nor remove any variable dropped.
  • Only instances of JDefaultModifier (the default modifier) will listen to VariablesUpdatedEvent (object state been modified) by default. This behaviour can be controlled with the new method setListenUpdates of AbstractModifier. This is useful if you have specific modifiers whose validators check state (for example, the parameter table of simpleFitsWriter requires a non-empty table).
  • If you want your parameter to support dropping a set of variables (multiple selection in Variables View) your parameter must be of type java.util.List (or Object). [Note that list or Pylist will not work]

Showing default values
By default task command generation for task GUIs generates the shortest equivalent task command invocation. You can see a user-readable representation of a task invocation in the Log View. "Full call" includes all optional parameters with defaults used, while "Command" is what is generated and appears in the Console View:
18 May 2012 14:35:36.930 INFO: [Full Call] asciiTableWriter(table={descript...3, c22]} [mandatory], file="/tmp/test.csv" [mandatory], formatterHeader=True [optional], warn=True [optional])
18 May 2012 14:35:36.946 INFO: [Command] asciiTableWriter(table=final2, file='/tmp/test.csv')
You can see the default values of parameters in:
  • The Log View upon task execution.
  • The Outline View when a task is selected in the Task View.
  • The Console View if you execute a print taskName.

Below the Task Preferences panel

Task Preferences

HIPE now supports the generation of task commands from GUIs with default values included: default values must be fed back in string form into Jython so these parameters must produce a valid Jython expression that maps to the default value. If that is not the case errors would be produced when invoking that task with full parameters. To avoid these errors, if there is not a converter different from the default one for the type of this parameter, the framework will generate a safe string (taskName.getParameter(paramName).defaultValue). This is not informative to the end user so, you should provide the Jython string representation of the default value of your parameter by using TaskParameter.setDefaultValueString(String).

Warning, important NOTE:

  • To fully support editing modifiers, that is, task parameter GUI elements that allow to generate/edit values (versus just passing the variables) must provide full support for generating Jython values. Most basic and usual types are supported but if this is not the case then you need to provide a Jython converter that supports your type.
  • The mechanism for showing defaults does not rely on toString() working for all types, so it generates safe Jython to avoid unwanted errors.
  • For composite types (arrays, collections...), the system may fall back to using toString() for the elements which may produce a bad default string. In this case, you have to provide a proper __repr__ or a Jython Converter for the element type.

Defining Jython converters
If the type of parameter you use does not have a good way to be written as valid Jython code (it does not implement __repr__ and it falls back to using toString() on the value from the selection) you may need to register a JythonConverter. For example, you have a parameter of type X and a modifier for X that has an editor. If you edit the value, when you press Accept the framework will try to generate a Jython string expression that creates that value to be assigned to the parameter in the task call. If there is no JythonConverter for class X, nor does it define __repr__ and X.toString() does not generate a valid (syntactically or type-wise) Jython expression, the generated command will not execute due to syntax errors or incompatibility with the type the parameter expects. In many cases, you can define a parameter of a simple type (Object or List) and do the conversion yourself to the proper type in the execute of your method. In others, you can just add a public PyString __repr__() method to the class. For example:
     // Java 
    public class WithRepr {
        String value;
        public WithRepr(String val) {
            value = val;
        }

        // This should return a PyString that when executed in Jython creates or gives access to an object like this one
        //   in this case we will construct a new WithRepr object 
        // Check the use of JythonConverters to deal with the constituent types 
        //   in this case a String, so that it is properly escaped, delimited ...
        // Note that this method implementation will not work with subclasses of this one, subclasses would have to override it
        //   solving this here also for subclasses would require that they agree on some common way of creating-accessing instances (constructors, a factory ...) 
        public PyString __repr__() {
            return new PyString("WithRepr(" + JythonConverters.convert(value) + ")");
        }

       // Typically for human consumption, so this cannot be properly parsed by Jython   
        @Override
        public String toString() {
            return value;
        }
    }

So, if your task has a parameter with an editable (that generates values, versus only using already defined variables) modifier the system may not generate the expected values for this type from the modifier. If that is the case, then the module that defines the type (class) of the modifier can:

  • Implement a JythonConverter.
  • Register it.

The rules for providing a JythonConverter are:

  • The type that needs the converter should define it (in the same module) if possible. Alternatively, the type can implement __repr__ (importing Jython).
  • If the module where the type is defined cannot import share_interpreter (where JythonConverter is defined) then, by default, it should be share_interpreter (or the "next" module "above" share_interpreter that already imports the type) the one that defines the converter.
  • A converter cannot be overwritten (but one can be registered for a subclass). This prevents that some client module can break more "basic" converters.
  • Extend AbstractJythonConverter instead of implementing directly the JythonConverter interface.
  • Your converter should call other converters for its elements (rather than doing it itself).

     // Java 
    JythonConverter<MyType> myConverter = new AbstractJythonConverter<MyType>() {        
        @Override
        public Class<? extends MyType> getType() {
            return MyType.class;
        }        
        @Override
        public String convert(MyType object) {
            return "MyType(" + JythonConverters.toJython(object.getArg()) + ")"; //if your type has a 1-arg constructor of a type already registered, for example
        }
    };

   JythonConverters.register(myConverter);

Implementing a modifier
Your modifier should extend AbstractModifier, which in turn implements the Modifier interface. Both reside in the ia.gui.apps.modifier package. The Modifier interface consists of two explicit contracts:
  • Support of drag and drop features (by indirectly extending setVariableSelection and getVariableSelection in the ia.gui.kernel package).
  • Support object inspection (via the setObject and getObject methods).

Modifiers must also honour two implicit contracts:

  • Deriving from JComponent.
  • If registered in the Extension Registry, providing an empty constructor.

Registering a modifier
If you want to share your modifier, you can register it via the Extension Registry with the following syntax (please note the name of the factory: factory.modifier):

# Jython 
from herschel.ia.gui.kernel import ExtensionRegistry, Extension
REGISTRY  = ExtensionRegistry.getInstance()
COMPONENT = ExtensionRegistry.COMPONENT

REGISTRY.register(COMPONENT,Extension(
        "MyModifier", # modifier name 
        "herschel.ia.mymodifier.MyModifier", # full class name of modifier
        "factory.modifier", # factory
        "herschel.ia.MyClass") # full class name of type supported (ex "java.lang.Float")

Be aware that the registration is system-wide, so it overrides any other registered modifier for that type. This means, if you are not careful, that you can break editing floats for all users... Additionally, the automatic selection in the registries is based on most specific type, so inheritance must be taken into account. That does not mean you should not share and register your modifiers, just that you should at least, communicate with the "owner" of the supported type, as the registration should be done close to where the supported type is defined (if it is in the JDK, that would be dp.core, for example).

In case the modifier you have created is only applicable to a specific task or even to a specific parameter of a specific task, you can simply create it and assign it to the applicable task parameter in getCustomModifiers:

    // YourTask constructor
    public YourTask() {
        addTaskParameter(new TaskParameter("someInput", MyClass.class));
        ...
    }

    // Customise your modifiers
    @Override
    public Map<String, Modifier> getCustomModifiers() {
	Map<String, Modifier> map = new LinkedHashMap<String, Modifier>();
	map.put("someInput", new MyModifier());
	return map;
    }


Warning, important Warning

Modifiers are GUI components, so they must be created in the Event Dispatching Thread (EDT). Modifiers built in the method getCustomModifiers are ensured to comply with this rule. Creating modifiers outside getCustomModifiers is dangerous and can cause misbehaviour or even GUI freezes. Specifically, creating modifiers in the task constructor is forbidden. For more details, see the Swing threading policy.

Signature components

In case the default input area based on modifiers does not fit your needs, you can replace it by your own implementation, and register it so that the system opens the task dialog with your custom TaskSignatureComponent. The following sections show you how this is done.

Implementing a task signature component

You can create a TaskSignatureComponent by extending JTaskSignatureComponent in the ia.task.gui.dialog package, and providing your own implementation of the makeModifierMap() method. Also if you want to also do your own layout you can extend AdbtractTaskSignatureComponent.

The JTaskSignatureComponent class implements the TaskSignatureComponent interface, which consists of four explicit contracts:

  • Support the setVariableSelection for initial assignment from the Tool Window.
  • Assign the Signature to display (setSignature).
  • Return a map of parameters and assigned values (in Map<TaskParameter, VariableSelection> getParameters).
  • Clear and check user inputs implementations (used by the default buttons).

Task signature components must also honour two implicit contracts:

  • Deriving from JComponent.
  • If registered in the Extension Registry, providing an empty constructor.

And, as a signature component acts as a container of modifiers, if you directly implement TaskSignatureComponent, you must also implement Disposable (to clean up the modifiers when your task GUI is closed):

  • Implement dispose() calling dispose on all your Modifiers that implement Disposable.

Conventions for labels for input parameters: to construct the labels of your parameters you can use the static function of class JTaskSignatureComponent

 public static JLabel getDecoratedLabel(TaskParameter tp, boolean isPrimeInput, String altName) 
it provides a decorated label (including tooltip) that follows the standard style. For the function to work properly your task parameters should be fully configured (for example, the parameter description will be the tooltip of the label) if present.

For example, if you want to use a custom Signature Component that just wants to use JFilePathModifier for a parameter aimed for a file name, you could do it like this:

public class MySignatureComponent extends JTaskSignatureComponent {

    private static final long serialVersionUID = 1L;

    protected Map<TaskParameter, Modifier> makeModifierMap() {

	SignatureApi signature = getSignature();
	Map<TaskParameter, Modifier> m = new LinkedHashMap<TaskParameter, Modifier>();

	m.put(signature.getTaskParameter("file"), new JFilePathModifier(SAVE));
	m.put(signature.getTaskParameter("number"), new JIntegerModifier());

	return m;
    }
}

Warning, important *NOTE:*
You no longer need a signature component to choose your own modifiers for your task (and link them with events...): Task has a new function

public Map<String, Modifier> getCustomModifiers()
where you can do just that, see above Registering a Modifier. Note that you can use this function to add listeners and special behaviours to your Modifiers.

Register a Task Signature Component

The registration of the Task Signature Component is done again in the __init__.py via the Extension Registry with the usual syntax (please note the name of the factory: factory.editor.tool.task.signature).

REGISTRY.register(COMPONENT,Extension(
        "Rotate Signature",
        "herschel.ia.task.example.RotateSignatureComponent",
        "factory.editor.tool.task.signature",
        "herschel.ia.image.RotateTask"))

See also the Extension Registry documentation for more details.

Laying out your task with one parameter per row (narrow GUI)

We provide a Task Signature Component that uses one parameter per row (for narrow panels). You just need to register that your task uses this signature component instead of the default one:
#1-column layout
REGISTRY.register(COMPONENT,Extension(
        "OpenVariableTask Signature",
        "herschel.ia.task.gui.dialog.JTaskSignatureComponentNarrow",
        "factory.editor.tool.task.signature",
        "herschel.ia.toolbox.util.OpenVariableTask"))

Custom Task Dialogs

Eventually, if the above options still do not accommodate your needs, you can replace the the default Task Panel with your own implementation

If this is the case you need to:

  • Implement a Task Panel.
  • Register it to the system.

Implement a Task Panel

The ia.task.gui.dialog.TaskPanel interface consists of three explicit contracts:
  • Support the setVariableSelection for initial assignment from the Tool Window.
  • Assign the Task to display (the setTask).
  • Notify request of executions to the framework by:
    • Allow for setting the Site Event handler for notifying request of execution (the setSiteEventHandler method).
    • Notify the execution requests calling the trigger method of herschel.ia.gui.kernel.SiteEventHandler passing a herschel.ia.gui.kernel.event.CommandExecutionRequestEvent.
      You can create this event through the herschel.ia.task.gui.dialog.TaskCommandExecutionEventFactory. Note that the factory uses preferences (General > Tasks > Generate task calls with a full set of parameters, to generate long or short forms for the commands).
  • Return the javax.swing.Actions for running the task and resetting/clearing the signature (the actions that are invoked when pressing "Accept" and "Clear" buttons, if present), to allow to execute them from the toolbar of HIPE. The simplest implementation would be to create and assign those actions to your buttons in your setTask(TaskApi) method and then to return them from the buttons when asked:
    public Action getRunAction() {
        return runbutton.getAction();
    }

    public Action getResetAction() {
        return resetButton.getAction();
    }
and the two implicit contracts inherited by the Extension Registry:
  • Be JComponent.
  • Have an empty constructor.
And, as a Task Panel acts as a container of modifiers (via a TaskSignatureComponent), if you directly implement , you must also implement Disposable (to clean up the signature when your task GUI is closed and to clean the viewer you are shown in):
    @Override
    public void dispose() {
        if (_component instanceof Disposable) { //_ component is a TaskSignatureComponent
            ((Disposable) _component).dispose();
        }
    }

The Rotate Panel example (herschel.ia.task.example.RotatePanel):
rotate_panel.jpg

Register a Task Panel

The registration of the Task Panel Component is done again in the __init__.py via the Extension Registry with the usual syntax (please note the name of the factory: factory.editor.tool.task.signature).

REGISTRY.register(COMPONENT,Extension(
        "Rotate Task Panel",
        "herschel.ia.task.example.RotatePanel",
        "factory.editor.tool.task",
        "herschel.ia.image.RotateTask"));

See also the Extension Registry documentation for more details.

Adding a Tool that is not a Task

If you have an existing task and want to make it available in HIPE, you just need to follow the steps described in the above section.

Now, a task has its limitations. It is somewhat an atomic operation for which you provide some inputs and expect some result.
Therefore, it is not expected for acting interactively with a user, and it is not meant for holding internal state either, that a user can modify during its execution.

If you need more flexibility, you can write your own implementation of the Tool interface.
Besides, you would most probably need a viewer associated to your tool, for letting the user interact with it.

This follows in some way the MVC pattern: your target data is the Model, your associated viewer is the View, and your tool is the Controller.

Tool Implementation

In order to write a tool, Tool interface needs to be implemented. Instead of doing it directly, it is encouraged to extend AbstractTool.

The information to be provided is passed to one of its constructors in a super call from the derived class:

    /** Constructor for a tool with a single parameter and general category. */
    protected AbstractTool(String name, Parameter primeInput)

    /** Constructor for a tool with a single input parameter. */
    protected AbstractTool(String name, Parameter primeInput, Category... categories)

    /** Constructor for a tool with multiple input parameters and general category. */
    protected AbstractTool(String name, Parameter primeInput, List<? extends Parameter> inputs)

    /** Constructor with all arguments. */
    protected AbstractTool(String name,
                           Parameter primeInput,
                           List<? extends Parameter> inputs,
                           Category... categories)
You provide the variable types you are interested in within the prime input: just return a herschel.ia.gui.kernel.ToolParameter initiated with the proper class of data you want to handle:
new ToolParameter("data", MyTargetDataType.class)

More conditions for checking whether the tool can react on a particular variable can be added by providing a ParameterValidator to the prime input.

The actual job to be done can be delegated to a third object (the "tool object"), or just be executed by the tool class itself.
This latter case is the default; otherwise, you need to call setToolObject(Object toolObject) in your constructor.

Moreover, you may return the categories you think the tool is meaningful for, by providing the proper ones in the super call.

Naming conventions

Conventions for names of a tool class and its variable in the Tasks view are similar than those for tasks. For example, a tool for spectrum filtering could be called:

     Name of the Class              : SpectrumFilterTool
     Name of the Tool (getName())   : spectrumFilter
     Name of the variable in Jython : spectrumFilter
 

Tool Viewer

Every tool has an associated viewer, which must implement EditorComponent (by extending AbstractEditorComponent or one of its subclasses).

Tool Registry

Once you have your tool and the corresponding viewer, you need to register them like this:
# Associate the tool with the viewer
REGISTRY.register(COMPONENT,Extension(
                 "Spectrum Filter Tool",
                 "herschel.path.to.SpectrumFilterToolComponent",
                 "factory.editor.tool",
                 "herschel.path.to.SpectrumFilterTool"))

# Register the tool so it is automatically available for the proper variables in HIPE
from herschel.ia.gui.kernel import ToolRegistry
from herschel.path.to import SpectrumFilterTool
spectrumFilter = SpectrumFilterTool()
ToolRegistry.getInstance().register(spectrumFilter)

__all__ = [ "spectrumFilter", "SpectrumFilterTool", ... ]

Communicating Tool & Viewer

In the viewer, you can access the tool and the selected data within the makeEditorContent method provided by AbstractEditorComponent.
At this point, you can let the tool know about the viewer as well, if you want:
protected boolean makeEditorContent() {

    // Get the tool and the selected data
    ToolSelection selection = getSelection();
    Tool   tool = selection.getTool();
    Object data = selection.getSelection().getValue();

    // Optional - you would need to provide a setViewer method
    ((MyTool)tool).setViewer(this);

    // Build the editor contents ...
}

Example

This simple reproducible example wraps up the just explained steps altogether.
It is just a button whose label is changed by the tool when the user clicks on it:

    1. The tool class

public class SimpleButtonTool extends AbstractTool {

    private ArrayData _data = null;
    private boolean _flag = true;

    public SimpleButtonTool() {
	super("simpleButton", new ToolParameter("data", ArrayData.class));
    }

    void setData(ArrayData data) {
	_data = data;
    }

    void updateLabel(JButton button) {
        boolean hasData = _data != null;
        button.setEnabled(hasData);
        if (hasData) {
            int size = _data.getSize();
            int rank = _data.getRank();
            button.setText("Data has " + (_flag? "size " + size : "rank " + rank));
            _flag = !_flag;
        } else {
            button.setText("No data selected");
        }
    }
}

    2. The viewer class

public class SimpleButtonToolComponent extends AbstractEditorComponent<ToolSelection> {

    private static final long serialVersionUID = 1L;
    private static int _counter = 1;
    private SimpleButtonTool _tool;

    protected Class<ToolSelection> getSelectionType() {
	return ToolSelection.class;
    }

    protected boolean makeEditorContent() {
	final JButton button = new JButton();
	setName("Button Tool " + _counter++);
        ToolSelection toolSelection = getSelection();
        _tool = (SimpleButtonTool)toolSelection.getTool();
        Selection selection = toolSelection.getSelection();
        if (selection != null) {
            _tool.setData((ArrayData)selection.getValue());
        }
        _tool.updateLabel(button);
	button.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
	        _tool.updateLabel(button);
            }
	});
	add(button);
	return true;
    }

    public Icon getComponentIcon() {
	return IconLibrary.VARIABLE;
    }
}

    3. The registration

COMPONENT = ExtensionRegistry.COMPONENT
REGISTRY  = ExtensionRegistry.getInstance()

REGISTRY.register(COMPONENT,Extension(
                 "Button Tool",
                 "herschel.path.to.SimpleButtonToolComponent",
                 "factory.editor.tool",
                 "herschel.path.to.SimpleButtonTool"))

from herschel.ia.gui.kernel import ToolRegistry
from herschel.path.to import SimpleButtonTool
simpleButton = SimpleButtonTool()
ToolRegistry.getInstance().register(simpleButton)

# cleanup
del(ExtensionRegistry, Extension, REGISTRY, COMPONENT)

__all__ = [ "simpleButton", "SimpleButtonTool" ]

    4. Executing the example

For executing this simple tool, just include it in a package owned by you, open the workbench in HIPE, and execute the following in the console:
x = Int1d.range(12)
y = Double2d([[1,2,3],[4,5,6]])
Then open the x and y variables with the Button Tool and click the button: its label is updated by the tool.

Triggering Events

For a full detailed section about triggering events have a look at DpHipeCommonUtilities.

Deprecating Tasks

Tasks provide support for ease of evolution. During a long project some functionalities are extended, reduced or even removed. The basic mechanism involves keeping a deprecated copy than informs, complains and guides until the removal. This is an extract of the information contained in the Task FAQ.

For tasks, the framework has been extended with:

  • A new String attribute deprecatedWith for tasks.
  • When executed, it will throw a warning and a log message.
  • Default GUI will show this message too.
  • TaskUtil.makeDoc(task) will add deprecation message too for removals. Note that doc is effectively "final" (only read once by Jython).
  • Task View will show deprecation info in the tooltip.
  • Outline will show the deprecation.

For task parameters, the framework has been extended with:

  • New String attributes deprecatedWith and deprecates for task parameters.
  • When executed, your preamble will "update" user code and log a warning.
  • Default GUI will not use deprecated parameters.
  • TaskUtil.makeDoc(task) will add deprecation info to deprecated parameters.
  • Task View will show deprecation info in the tooltip.
  • Outline will show the deprecation.

Use cases:

  • Changing names of parameters.
  • Changing the name of a task.
  • Removing a task.

Changing names of parameters

For a more detailed analysis (outputs) see Task FAQ. Here we assume it is an input parameter. It involves keeping the old parameter deprecated during a release:
  • Rename the parameter to the NEWNAME.
  • Copy the parameter at the end of the signature (with the old name OLDNAME) as optional and without a default value and set the value of the deprecatedWith attribute.
  • Add a preamble to your task that copies values from OLDNAME parameter to NEWNAME parameter and logs warnings. It should call the parent preamble at the end.
  • Add info to the Registry of removed names for taskname.OLDNAME and do not forget to update JExamples and JTags (or other documentation).

Changing the name of a task

  • Rename the task (NEWNAME, both in source and init.py).
  • Add another instance in the init.py , changing the name (OLDNAME) and deprecatedWith attributes. Export this "new" (but with the old name) task instance, but do not register it.
  • Add info to the Registry of removed names either now or when you finally remove the OLDNAME instance.

Removing a task

  • Deprecate the code.
  • Set deprecatedWith before calling makeDoc.
  • Remove registration from init.py (but keep imports).
  • Once the deprecation period is over, all the code (source, init) will be removed and info added to the Registry of removed names.


blog comments powered by Disqus
Topic attachments
I Attachment History Action Size Date Who Comment
PNGpng Screen_Shot_2014-02-18_at_13.48.36.png r1 manage 55.8 K 2014-02-18 - 12:46 AlvarGarcia Task Preferences panel (updated to HIPE 13)
PNGpng TaskOutline.png r1 manage 27.2 K 2012-05-18 - 12:59 JavierDiaz a task showing its outline
PNGpng TaskPreferences.png r1 manage 22.3 K 2012-05-18 - 12:55 JavierDiaz Task Preferences panel
PNGpng asciiTableReader_Advanced.png r1 manage 21.0 K 2012-05-08 - 09:39 JavierDiaz Task dialogs with groups (group 2)
PNGpng asciiTableReader_basic.png r1 manage 11.7 K 2012-05-08 - 09:38 JavierDiaz Task dialogs with groups (group 1)
PNGpng crop.png r1 manage 13.7 K 2009-09-29 - 15:55 JaimeSaiz  
PNGpng crop_closed.png r1 manage 8.1 K 2010-06-02 - 11:55 JavierDiaz crop with closed sections updated to the new look of tasks
PNGpng crop_input.png r1 manage 14.5 K 2009-09-29 - 16:09 JaimeSaiz crop task with input highlighted
PNGpng crop_input_new.png r1 manage 13.8 K 2010-06-02 - 11:54 JavierDiaz crop input updated to the new look of tasks
PNGpng crop_modifier.png r1 manage 14.4 K 2009-09-29 - 16:09 JaimeSaiz crop task with modifier highlighted
PNGpng crop_modifier_new.png r1 manage 13.7 K 2010-06-02 - 11:53 JavierDiaz crop modifier updated to the new look of tasks
PNGpng crop_new.png r1 manage 13.9 K 2010-06-02 - 11:55 JavierDiaz crop updated to the new look of tasks
PNGpng rotate_new.png r1 manage 10.6 K 2010-06-02 - 11:52 JavierDiaz rotate panel updated to the new look of tasks
PNGpng tasks.png r1 manage 13.0 K 2009-09-29 - 16:11 JaimeSaiz Tasks and variables
Edit | Attach | Watch | Print version | History: r113 < r112 < r111 < r110 < r109 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r113 - 2014-02-18 - AlvarGarcia
 
This site is powered by the TWiki collaboration platform Powered by Perl