Difference: DpHipeTools (112 vs. 113)

Revision 1132014-02-18 - AlvarGarcia

Line: 1 to 1
 
META TOPICPARENT name="WritingTasks"
<-- ANALYTICS CODE - DO NOT EDIT -->
<-- Google Analytics script BEGIN -->
<-- Google Analytics script END -->
Line: 203 to 206
  rotate_new.png

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

Changed:
<
<
  • Parameter modifiers
  • Signature components
  • Custom task panels
>
>
  • Parameter modifiers.
  • Signature components.
  • Custom task panels.
 
Line: 219 to 222
  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.
Changed:
<
<
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.
>
>
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:
Changed:
<
<
  • 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 (using a specific constructor, for example) or add listeners between them (to update one when another is updated, for example) you will also have to use Task.getCustomModifiers()
>
>
  • 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.
Changed:
<
<
  • 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 simpleFitsWriter 's table parameter 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: this may be obsolete since Jython 2.5?]
>
>
  • 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
Changed:
<
<
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 optionals with defaults used, while "Command" is what is generated and appears in the Console View:
>
>
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:
Changed:
<
<
  • 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
>
>
  • 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
Changed:
<
<
TaskPreferences.png
>
>
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).

Changed:
<
<
Warning, important
  • To fully support editing modifiers, that is, task parameter GUI elements that allow to generate-edit values (vs just pass variables) full support for generating Jython values must be provided. 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.
>
>
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.
Changed:
<
<
  • 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.
>
>
  • 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
Changed:
<
<
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.
>
>
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.
Changed:
<
<
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:
>
>
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 {

Line: 291 to 294
 
Changed:
<
<
So, if your task has a parameter with an editable (that generates values, vs 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
>
>
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.
 
Changed:
<
<
The rules for providing JythonConverters are:
>
>
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.
Line: 323 to 326
 
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:
Changed:
<
<
  • 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)
>
>
  • 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:
Changed:
<
<
  • Deriving from JComponent
  • If registered in the Extension Registry, providing an empty constructor
>
>
  • Deriving from JComponent.
  • If registered in the Extension Registry, providing an empty constructor.
 

Registering a modifier
Line: 348 to 351
  "herschel.ia.MyClass") # full class name of type supported (ex "java.lang.Float")
Changed:
<
<
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 ... Also that as the automatic selection in the registries is based on most specific type, 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).
>
>
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:
Line: 387 to 390
 
You can create a TaskSignatureComponent by extending JTaskSignatureComponent in the ia.task.gui.dialog package, and providing your own implementation of the makeModifierMap() method.
Changed:
<
<
Also if you want to also do your own layout you can extend ==AdbtractTaskSignatureComponent.
>
>
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:
Changed:
<
<
  • 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)
>
>
  • 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:
Changed:
<
<
  • Deriving from JComponent
  • If registered in the Extension Registry, providing an empty constructor
>
>
  • Deriving from JComponent.
  • If registered in the Extension Registry, providing an empty constructor.
 
Changed:
<
<
And, as a signature component acts as a container of modifiers, if you directly implement TaskSignatureComponent, you must also implement Disposable (to clean the modifiers when your task GUI is closed):
  • implement dispose() calling dispose on all your Modifiers that implement Disposable.
>
>
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.
 
Changed:
<
<
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.
>
>
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.
 
Changed:
<
<
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:
>
>
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 {

Line: 426 to 429
 
Changed:
<
<
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 "Register a Modifier". Note that you can use this function to add listeners and special behaviours to your Modifiers.
>
>
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

Changed:
<
<
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).
>
>
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",

Line: 466 to 469
 

Custom Task Dialogs

Changed:
<
<
Eventually, if the above options still do not accommodate your needs you can replace the the default Task Panel with your own implementation
>
>
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:
Changed:
<
<
  • Implement a Task Panel
>
>
  • Implement a Task Panel.
 
  • Register it to the system.

Implement a Task Panel

Changed:
<
<
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 (Editors & Viewers/Task Dialog/fullCommand to generate long or short forms for the commands).
  • 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:
>
>
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();

Line: 491 to 494
  return resetButton.getAction(); }
Changed:
<
<
and the two implicit contracts inherited by the Extension Registry
  • Be JComponent
  • Have an empty constructor
>
>
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

Line: 506 to 509
 
Changed:
<
<
The Rotate Panel example (herschel.ia.task.example.RotatePanel):
>
>
The Rotate Panel example (herschel.ia.task.example.RotatePanel):
  rotate_panel.jpg
Line: 514 to 517
 

Changed:
<
<
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).
>
>
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",

Line: 745 to 748
 

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.
Changed:
<
<
This is an extract of the information contained in the Task FAQ
>
>
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.
Line: 764 to 767
 
  • Outline will show the deprecation.

Use cases:

Changed:
<
<
  • Changing names of parameters
  • Changing the name of a task
  • Removing a task
>
>
  • Changing names of parameters.
  • Changing the name of a task.
  • Removing a task.
 

Changing names of parameters

Changed:
<
<
For a more detailed analysis (outputs) see Task FAQ. Here we assume it is an input parameter. It involves keeping the old 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
>
>
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.
Changed:
<
<
  • Add info to the Registry of removed names for taskname.OLDNAME and do not forget to update JExamples and JTags (or other documentation)
>
>
  • 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

Changed:
<
<
  • Rename the task (NEWNAME, both in source and init.py)
>
>
  • 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

Changed:
<
<
  • Deprecate the code
  • Set deprecatedWith before calling makeDoc
  • Remove registration from init.py (but keep imports)
>
>
  • 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.

 
This site is powered by the TWiki collaboration platform Powered by Perl