Tools are processing units that operate on specific data elements.
interface.
Tasks are examples of tools within HIPE. In this case,
is used under the hood. See the topic on
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 non-task tools) or a dialogue window for settings parameters (for task tools).
You can made you 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()
To make your task appear in the Tasks view, you need to add the following lines to the Jython file:
from herschel.ia.task.views import TaskToolRegistry
toolRegistry = TaskToolRegistry.getInstance()
toolRegistry.register(compute)
You can also specify that your task belongs to one or more Category
:
from herschel.ia.gui.kernel.Tool import Category
toolRegistry.register(compute, [Category.IMAGE, Category.PACS]))
Your task is now enabled whenever the users selects a session variable of the same type as the first input parameter of your task.
To define the prime parameter, modify your task constructor as follows:
class ComputeTask extends Task {
ComputeTask() {
super("compute");
prime = new TaskParameter("spectrum", SpecificProduct.class)
:
getSignature().setPrimeInput(prime)
}
}
Tasks Naming Conventions
Tasks registered in HIPE should follow the naming conventions shown in this example:
- Name of the class:
ReduceTask
- Name of the task (given by
getName()
): reduce
- Name of the task variable in Jython:
reduce
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.
You can register your modifier via the Extension Registry with the following syntax (please note the name of the factory: factory.modifier
):
REGISTRY.register(COMPONENT,Extension(
"MyModifier",
"herschel.ia.mymodifier.MyModifier",
"factory.modifier",
"herschel.ia.MyClass")
Be aware that the registration is system-wise, so it overrides any other registered modifier for that type.
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 assign it to the applicable task parameter:
// 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;
}
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 to the system. The following sections show you how this is done.
Implementing a task signature component
You can create a task signature component by extending JTaskSignatureComponent
in the ia.task.gui.dialog
package, and providing your own implementation of the makeModifierMap()
method.
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
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 ia.gui.apps.modifier.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;
}
}
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 "Register a Modifier".
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.
Custom Task Dialogs
Eventually, if the above options still do not accommodate you 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 ia.gui.kernel.SiteEventHandler passing a ia.gui.kernel.event.CommandExecutionRequestEvent.
You can create this event through the ia.task.gui.dialog.TaskCommandExecutionEventFactory.
- Return the
javax.swing.Actions
for running the task and resetting (clear) 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
The Rotate Panel example (herschel.ia.task.example.RotatePanel):
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 status 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 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 ia.gui.kernel.parts.EditorComponent (by extending ia.gui.kernel.parts.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 ...
}
Simple sample
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;
private boolean _flag = true;
public SimpleButtonTool() {
super("simpleButton", new ToolParameter("data", ArrayData.class));
}
void setData(ArrayData data) {
_data = data;
}
void updateLabel(JButton button) {
int size = _data.getSize();
int rank = _data.getRank();
button.setText("Data has " + (_flag? "size " + size : "rank " + rank));
_flag = !_flag;
}
}
2. The viewer class
public class SimpleButtonToolComponent extends AbstractEditorComponent<ToolSelection> {
private static final long serialVersionUID = 1L;
private static int _counter = 1;
private SimpleButtonTool _tool;
public SimpleButtonToolComponent() {
super(new BorderLayout());
}
protected Class getSelectionType() {
return ToolSelection.class;
}
protected boolean makeEditorContent() {
final JButton button = new JButton();
setName("Button Tool " + _counter++);
_tool = (SimpleButtonTool)getSelection().getTool();
_tool.setData((ArrayData)getSelection().getSelection().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.