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!

Common utilities in HIPE

This section explains additional useful information for HIPE developers, both for contributors with views, editors, etc. and for maintainers of the HIPE framework.

HIPE contributors

This section is aimed as a complement for developers contributing to HIPE by writing their own views, editors, tasks, etc.

If you are a maintainer of the HIPE framework, you might find useful the section below as well.

The following advice includes common development in a Java application and some HIPE specific matters.

Choose appropriate log levels

Log messages written with java.util.logging.Logger (Java 6) for HIPE 11 or older or java.util.logging.Logger (Java 7) for HIPE 12 or newer are shown in the Log view.

Please choose the log level appropriately, i.e.

  • Avoid SEVERE level when the problem is not so relevant, for example that your view miss some input, or a parameter is not totally correct. This would only affect your view, but it would still work (maybe in a degraded way) and, moreover, HIPE as an application will still work. Use WARNING or INFO for these cases.

  • The other way around, if the information is relevant for the user, don't hide it with FINE or lower levels, since they are almost always filtered out.

Note that the upper log levels (including INFO) should never be used for developer related data, see Log use (internal resource).

Make HIPE responsive

Let the Stop button work

One of the hot topics when working with HIPE is the Stop button, which is meant for cancelling the current job. Letting it work properly is a cooperative issue.

When talking about cancellation in HIPE, we may divide three main areas:

  1. Jython command/s (a whole script or pipeline lies in this category). This can be interrupted with the Stop button, unless the currently executing code ignores the interruption request. Normally, such ignorance of an interruption request would only happen in...

  2. Tasks. Long tasks should have check points for interruption requests. Since tasks are run by the interpreter, pressing the Stop button in this case would cancel the task in such intermediate check point, if existing.

  3. GUI actions. Code executed in the EDT (Wikipedia article) should provide fast response. If some long job is needed, javax.swing.SwingWorker can be used, which also provides means for cancelling the work (see its cancel method). A worker can be used in combination of BusyJob, which also supports cancellation. Busy jobs can be stopped with the Stop button with no extra code.

In summary:

  • Plain Jython commands do not need to be aware of interruption; the interpreter takes care of it.
  • Long tasks should follow the guidelines for being cancellable in the middle of their execution.
  • Code executed in the EDT should be fast enough that it shouldn't block the GUI.

Avoid freezes

Apart of low responsiveness due to previous issues (long tasks that don't respond to interruption requests, heavy computations done in the EDT...), in all Java GUI tools there is a risk of hanging the entire application, so the only corrective action is to kill and restart it.

The most dangerous GUI freezes are the deadlocks: the EDT is waiting for some job to be done in other thread, while this thread is waiting for the EDT doing something.

Some hints to avoid deadlocks:

  • Execute your GUI code in the EDT. Take into account that some callbacks to listeners are run in other threads (like CommandExecutedEvent). When in doubt, execute any code related to your GUI in a call to SwingUtilities.invokeLater or SiteUtil.execLater. Try to avoid calls to SwingUtilities.invokeAndWait, since they are potential cause for deadlock.

  • Beware that PlotXY class forces to be executed in the EDT, by calling SwingUtilities.invokeAndWait or SwingUtilities.invokeLater. You can safely call any plot method from any thread, only when the thread does not hold an EDT lock.

  • Avoid calling SiteUtil.execAndWait(String command) and its variants from EDT even when the command does not perform any GUI stuff, since it could lead to a deadlock. If you want to execute a Jython command from EDT that involves any GUI operation, please call SiteUtil.execLater(String command) or use a SwingWorker.

Further information

More advice can be found in the presentation Developing GUIs in HIPE PDF.

Know available utilities

The Java Development Toolkit provides lots of libraries that developers coming from other languages like C/C++ find wonderful, because almost all basic funcionality in these languages need to be re-invented, copied or buyed.

The Herschel Java code base has plenty of common utilities as well. Knowing them could help you in developing your tools with much less effort.

Here is a list of some of these common packages:

Here are some classes that can be highlighted among them. Please consult their Javadoc for more information.

Package Class Description
herschel.ia.gui.apps.components.util BottomPanel Panel whose contents occupy all the available space from its position to the panel's container's bottom.
ColumnsPanel Panel organizing components in columns.
Dereferencer Loads an object or resource in the background, by informing HIPE of its busy status.
ListModelAdapter Adapter of a ListModel to a ComboBoxModel.
FoldingSectionsPanel Panel with sections in a vertical layout, where each section can be folded (hidden) or unfolded (shown) by the user with single clicks.
herschel.ia.gui.apps.components.variable AbstractVariableTree Holds a VariableSelection that can be represented in a tree, triggering SelectionEvent when a node is clicked, providing a popup menu for opening children nodes, etc.
herschel.ia.gui.kernel.util ImageUtil Fit an image to the given dimensions, make an image from a Java component.
TableUtil Resize columns and rows of a JTable according to its contents, allow sorting the table by clicking on a column, by also letting to unsort the table afterwards.
TreeUtil Some utilities for managing a JTree.
herschel.share.io IOUtil Converts objects into byte array back and forth, through serialization, taking into account also objects handled by resolvers (like Product, Dataset, etc.).
FileUtil Write and read text files in a line of code, avoid Windows' path separator problems in Jython commands, and more.
ResolverRegistry Registers resolver instances that know how to serialize or deserialize objects not implementing Serializable.
herschel.share.interpreter InterpreterUtil Utility methods regarding Jython.
InterpreterNameSpaceUtil More on the same area.
JythonConverters Generates the Jython code that would create a given object.
herschel.share.util ClassMap Specialization of Map holding classes as keys, where values can be found taking into account class hierarchies of the key classes.
InvertibleMap Specialization of Map that allows getting keys from values too.
ObjectUtil Test of object equality taking nulls into account, straightforward implementation of hashCode(), create instances from class names with nice handling of exceptions, non wordy casts...
StringUtil Fit a string to a given length, escape characters, flatten and unflatten string collections, encode and decode URLs...
WeakHashSet Specialization of Set holding WeakReference internally, which means that they can be removed in the background if no pointed by any other reference. Useful for listener collections.

This list can be expanded; feel free to enrich it with more useful classes (please respect the alphabetical order when doing so).

More information about this topic can be found in DpHipeCommonUtilities.

How to ...

This section explains how to code some common actions in HIPE.

Trigger events

The standard way of triggering events is to get a reference to the SiteEventHandler interface, which contains all related methods to the HIPE event system. This is typically done via the ViewPart interface that is passed on through the init method of the Viewable.

    private void someMethod() {
        :
        getPart().getEventHandler().trigger(aSiteEvent);
        :
    }

However there are cases where you do not have access to a Viewable and you still want to trigger events for informing the rest of the system about something. In order to achieve this you can use a utility method available in a dedicated utility class.

If no view part is accessible from your code, then you can choose one of the SiteUtil.trigger methods.

Execute Jython statements

Sending execution events

HIPE has a dedicated Event for asking the execution of Jython statement: CommandExecutionRequestEvent. The event is served by the ConsoleView and the result of its execution is visible both in the screen of the ConsoleView and in the list of the HistoryView (but you don't need to know about these views and their api).

Every time a CommandExecutionRequestEvent event is served, HIPE generates an equivalent CommandExecutedEvent tracing the original CommandExecutionRequestEvent, so you can easily synchronize on the completion of the execution. However, this is not needed either, since SiteUtil class provides a convenient method for doing it: execAndWait.

    private void someMethod() {
        :
        // this is the reference to the modifier
        Command c = SiteUtil.execAndWait(this, "print 'something'");
        :
    }

The execAndWait method returns a Command tracing all the information about its execution including the statement result (isSuccesful()).

Last but not least, SiteUtil contains other utility methods that allow for more options including a un-synchronized execution method. See SiteUtil for the full detail.

A Jython Example

This example provides a short script for testing the SiteUtil execution functionality directly from the console. Please note:

  • the reference used is _jconsole, which is a name available in jython and pointing to the jconsole (indeed visible).
  • the call is synchronous and therefore it stops the gui thread for the time of the execution (see Lesson: Concurrency in Swing for a general discussion about GUI and threads).

from herschel.ia.gui.kernel.util import SiteUtil
from javax.swing import *

frame = JFrame("Hello Jython")

def perform(event):
   SiteUtil.execAndWait(_jconsole, "2*2", "A")
   
button = JButton("Press me", actionPerformed = perform)

frame.add(button)
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
frame.setSize(300, 300)
frame.show()

Create variables

During the processing of a particular view, it may be wanted to create a variable, so that it is shown in the Variables view and the user can do a further work with it.

Creating a variable can be done either with or without echo to the console.

With echo to the console

Just use one of the methods provided by SiteUtil:

  • Synchronous call: Command result = SiteUtil.execAndWait("yourVariable", "Something()");  // result will inform about the executed command

  • Asynchronous call: SiteUtil.execLater("yourVariable", "Something()");  // request execution to the interpreter but return immediately

Tip, idea Prefer this way when you want the variable creation be recorded in the history, so it can be reproduced later.

Example: For the PolygonHistogramPanel we need to construct a Double1d with the corners of the polygon (which are listed in Double1d edges) and add it to the variables map of the task panel:

        Command c = SiteUtil.execAndWait("Double1d(" + edges.toString() + ")", "pyedges");
	String name = c.getOutputName();
	Object value = c.getOutputValue();
	VariableSelection edgesPixel = new VariableSelection(name, value);
	getMap().put(getTask().getSignature().getTaskParameter("edgesPixel"), edgesPixel);

Without echo to the console

In this case, you can create the variable with any Java code (not to be executed by the interpreter), and then publish it like this:

Object variable = ...;
VariablesUtil.addVariable("yourVariable", variable);

Tip, idea Prefer this way when the creation of the variable is so complex that doing it with Jython commands would be too difficult.

Show file choosers

Selecting a file is a common user task. Java Swing comes with JFileChooser, which can be configured in many ways. It even allows you to provide file filters so the user can select among a defined set of them.

Now, leaving each code to show and create its own file chooser can lead to inconsistencies. For example, in one place a FITS file can be considered any file ending with .fits, while other code can be more wide and accept also extensions like .fts or .fits.gz.

In order to provide a unified way of showing file choosers and define file filters, you should use the FileChooser class provided by HIPE. The following lines explain more about this.

Define file types

A file type is normally determined by its extension. HIPE allows to define a file type either by a list of possible extensions (for example, jpg, png, gif for image files) or by a regular expression.

In order to define a file type, you need to:

1. Create a class extending java.io.File.

2. Register that class as a file type in the extension registry. This can be achieved in a __init__.py file with lines like the following:

from herschel.ia.gui.kernel import ExtensionRegistry, Extension
from herschel.ia.gui.kernel.util import FileType

FILE_TYPE = FileType.FILE_TYPE
REGISTRY  = ExtensionRegistry.getInstance()

# Register the image file type
REGISTRY.register(FILE_TYPE, Extension(
                 "site.view.navigator.image",               # file id; navigator stands for the Navigator view (which shows the file trees)
                 "herschel.some.package.ImageFile",         # class extending File
                 "jpg,png,gif",                             # comma-separated list of extensions
                 "herschel/some/package/Image.gif"));       # icon associated to this file type

# Cleanup
del(ExtensionRegistry, Extension, FileType, REGISTRY, FILE_TYPE)

If a regular expression is used instead, it must be prefixed by regex: and follow the pattern syntax explained in java.util.regex.Pattern (Java 6) for HIPE 11 or older or java.util.regex.Pattern (Java 7) for HIPE 12 or newer. For example:

REGISTRY.register(FILE_TYPE, Extension(
                 "site.view.navigator.obsDescriptor",       # file id; navigator stands for the Navigator view (which shows the file trees)
                 "herschel.some.package.ObsDescriptorFile", # class extending File
                 "regex:obs-[\\d]+[.]xml",                  # regular expression
                 "herschel/some/package/Observation.gif")); # icon associated to this file type

Use file choosers

herschel.ia.gui.kernel.util.component.FileChooser extends javax.swing.JFileChooser with additional functionality:

  • File filters for defined file types as explained above can be added with the setFileFilters method.
  • If the dialog is opened with the save mode and an existing file is chosen, it warns the user and ask for overwriting. Being this the default behaviour, it can be disabled.
  • File choosers can be shared so that the user sees them opened in the previously chosen directory. The global file chooser can be retrieved with FileChooser.getChooser(). Custom file choosers can be obtained with FileChooser.getChooser(owner), which keeps also their state among calls.
  • If you want to avoid the boilerplate code for creating, configuring, showing and getting the result of a file chooser, you can use one of the static methods FileChooser.getFile.
  • Configuration of file choosers is made easier by means of the auxiliary FileChooser.Configurer class.

Consult the Javadoc for additional details.

Select files in task GUIs

Some task parameters can expect a file. In these cases, the parameter type is usually String rather than File, to make its call easier from the console. For example, compare

someTask(file="/path/to/file.txt")

with the wordier

someTask(file=java.io.File("/path/to/file.txt"))

However, the default modifier (GUI component) for a string parameter is a plain text field.

In order to show the text field along with the more handy button with a folder icon that shows a file chooser and updates the field with the chosen file, you can use JFilePathModifier.

For more information on modifiers, refer to the tasks and tools documentation.

Show popup dialogs

The PopupDialog class

The Java Swing library provides a class for showing popup dialogs: JOptionPane (Java 6) for HIPE 11 or older or JOptionPane== (Java 7) for HIPE 12 or newer.
This class is used in different modules along the project.

Now, in order to unify the look and feel of dialogs, and to simplify the API as well (the calls to !JOptionPane are normally too long), there is a wrapper class which should be used for showing popup dialogs in HIPE: PopupDialog.

Using this class we ensure that we use HIPE-like icons instead of Java-like icons:

Dialogs with HIPE-like icons

hipe-confirmation.png hipe-error.png hipe-warning.png

Dialogs with Java-like icons

java-confirmation.png java-error.png java-information.png

Help You can use PopupDialog as well if your component is used by both HIPE and JIDE. It internally checks whether to show the HIPE style icons or the Java standard ones.

Sample Java code

The API is easy and self-explanatory, just consult PopupDialog.

For example, the first dialog above could be generated with the following code:

int option = PopupDialog.YES_NO_CANCEL_OPTION;
String title = "Unsaved changes";
String message = fileName + " has unsaved changes.\nSave before closing?";
int res =  PopupDialog.showConfirm(this, message, option, title);
if (res == PopupDialog.YES) {
    saveFile();
}
if (res != PopupDialog.CANCEL && res != PopupDialog.CLOSED) {
    closeEditor();
}

Load icons

Icons are widely used in HIPE.

Instead of needing to work with the Java API directly, when you want to load icons, HIPE provides some utilities for making the job easier:

  • herschel.ia.gui.kernel.util.IconLibrary provides some static icons already loaded. If the icon you want to use is there, you are done.
  • herschel.ia.toolbox.spectrum.explorer.actions.IconLoader provides static methods for loading an icon.

If you want to use one of the icons in ia_gui_kernel, call getKernelIcon, if you want to load an icon located in your own module, use any of the variants of IconLoader.getIcon.

The IconLoader caches already loaded icons, so further accesses to them is faster: access to disk is only performed the first time. Therefore, it is not needed that you create your own icon constants.

Debug GUI problems

As part of giving better support to developers and to help diagnose GUI problems, a Debug preferences category has been added to HIPE.

The preference Enable debug in that new category shall be switched on to let debug services be run individually. When this preference is set, debug services will also be controllable through the Tools -> Debug menu, which will be invisible while that master preference is unset. Current options for debug:

  • Check the painting of graphical components: it checks that all painting is done in the EDT. If it is not the case then you have to move code to the EDT.
  • Check that the application is responsive: checks that no event in the EDT takes more than a second to process and that threads are not (inter)blocked. If it is the first case then you need to move code out of the EDT. If it is the second case then you need to redesign shared resource access.
  • Paint special border on the component pointed at: this helps to diagnose GUI layout problems.

Provide an image preview of a FITS file

When the user selects a FITS file in the Navigator view, the Outline view shows few of the most relevant meta data.

It tries also to make an image preview of the FITS file. For this, it looks for a PreviewMaker that can cope with the product type that is returned by FitsReaderTask.

If you develop SomeProduct (which could be an image, a spectrum, a cube...) and want to provide an image for it in the outline, look at how to do it.

Save files in the HIPE directory

If your module needs to save hidden files in the user disk, consider doing it under the HIPE directory, so the user home is not flooded with many different directories.

The HIPE directory is ~/.hcss/apps/hipe, but it used to be ~/.hipe.

In order to avoid depending on such changes, use SiteFileUtil.getSiteDirectory() or SiteFileUtil.getSiteDirectory(String area), which handle the actual file path properly.

By using these methods of herschel.ia.gui.kernel.util.SiteFileUtil, your code would be protected against changes in the HIPE directory. You wouldn't even need to worry about moving data from the old location to the new one; it would be done transparently.

If your module generates many files that need periodic clean up, consider also registering in the herschel.ia.gui.kernel.util.FileCleaner.

Persist data among sessions

HIPE remembers some user customizations that are not expicitly saved in preferences: perpective layouts, last opened perspective, recent files, opened editors, etc.

These are not saved as preferences because they have no corresponding preferences panel nor preferences category and key. They are persisted through the session persistor framework.

If you want to store data at application exit like these, for being recovered at next HIPE start up, you can do it yourself by writing files within a site area (see previous tip).

However, using the session persistor framework instead may save you some coding. Moreover, your session items would be exported and imported automatically with the File -> Session -> Export / Import options for free.

The following example shows how to use it:

import herschel.ia.gui.kernel.session.SessionItem;
import herschel.ia.gui.kernel.session.SessionPersistor;

// This viewer has a split pane; we want to persist the separator location
// so the user doesn't need to relocate it each time the viewer is opened
public class SplitViewer {

    private SessionItem<Double> splitLocation =
        new SessionItem<Double>("site.split.viewer.location", Double.class, 0.25); // initialize with default value

    // Constructor
    public SplitViewer() {
        SessionPersistor.getInstance().register(splitLocation);  // this reads the previous value, if existing
        double location = splitLocation.getValue();
        // use location for setting the split division
    }

    // Listen to changes in the divider for updating the location
    public void splitLocationChanged(double location) {
        splitLocation.setValue(location);  // this will be saved automatically at normal exit
    }
}

SessionItem may hold a value of any of the following types: Integer, Long, Float, Double, Boolean, String or byte[].

If you want to persist an item of other type, you can do it by means of SerializableItem instead, provided that the object can be serialized, that is, it implements java.io.Serializable or is managed by one of the resolvers of herschel.share.io.

For more information and options of these classes, see their Javadoc.

Register removed names

Obsolete tasks, functions, etc. that are removed in a given version of HIPE but are still present in users' scripts may confuse them, because they worked in the previous release.

The error message

<type 'exceptions.NameError'>: name 'someRemovedName' is not defined

does not help much on how to proceed.

To overcome this problem, the removed name can be registered so that useful information can be given to the user upon name errors on it.

The registration should be done in the __init__.py file of the package where it was defined, like this:

from herschel.share.interpreter import ObsoleteNames
ObsoleteNames.register(removedName, version, newName, reason)  # the new name and the reason are optional

For example, if a function oldSlowAlgorithm is removed in HIPE 9.0, and a newFastAlgorithm is created in its stead, it should be recorded like this:

from herschel.share.interpreter import ObsoleteNames
ObsoleteNames.register("oldSlowAlgorithm", "9.0", "newFastAlgorithm", "The algorithm was intrinsically slow")

which would produce the following output when oldSlowAlgorithm is called:

HIPE> oldSlowAlgorithm(data)
Error: The obsolete name 'oldSlowAlgorithm' has been removed in HIPE 9.0
Reason: The algorithm was intrinsically slow
Please use 'newFastAlgorithm' instead

HIPE framework

This section is meant for helping developers of the HIPE framework.

Got lost in the Java code? Below some information that you might find useful.

Design

In the Architecture Desing Document (ADD, included in HIPE Developer's Reference Manual) there is an introduction to the terminology and how classes are related to the functionality.

Modules and classes

Besides, the following table can help you further as a quick guide.

Functionality Module Some related types
Editor area
 
ia_gui_kernel
 
herschel.ia.gui.kernel.parts.EditorArea
herschel.ia.gui.kernel.parts.impl.EditorAreaImpl
Menus & toolbars
 
ia_gui_kernel
 
herschel.ia.gui.kernel.menus.MenuManager
herschel.ia.gui.kernel.menus.MenuManagerImpl
Look & feel ia_gui_kernel herschel.ia.gui.kernel.parts.impl.AbstractSite
Start-up
 
 
ia_gui_apps
ia_gui_kernel
 
herschel.ia.gui.apps.HipeStarter
herschel.ia.gui.kernel.SiteApplication
herschel.ia.gui.kernel.util.SplashWindow
Welcome pages ia_gui_apps herschel.ia.gui.apps.views.welcome.WelcomeView
Navigator ia_gui_apps herschel.ia.gui.apps.views.navigator.NavigatorView
Outline
 
 
ia_gui_apps
 
 
herschel.ia.gui.apps.views.outline.OutlineView
herschel.ia.gui.apps.views.outline.OutlineComponent
herschel.ia.gui.apps.components.outline.*
Script editor
 
ia_jconsole
 
herschel.ia.jconsole.views.JythonEditorComponent
herschel.ia.jconsole.views.TextEditorComponent
Console
 
 
ia_jconsole
 
 
herschel.ia.jconsole.views.ConsoleView
herschel.ia.jconsole.jython.Interpreter
herschel.ia.gui.kernel.command.CommandExecutor
Variables view
 
ia_gui_apps
 
herschel.ia.gui.apps.views.variables.VariablesView
herschel.ia.gui.apps.views.variables.NameSpaceTree
Tasks view
 
ia_task
 
herschel.ia.task.views.TasksView
herschel.ia.task.views.TaskTree


blog comments powered by Disqus
Topic attachments
I Attachment History Action Size Date Who Comment
PDFpdf Developing_GUIs_in_HIPE.pdf r1 manage 386.1 K 2011-11-24 - 13:59 JaimeSaiz  
PNGpng hipe-confirmation.png r1 manage 11.1 K 2008-08-19 - 11:47 JaimeSaiz Hipe-like confirmation dialog
PNGpng hipe-error.png r1 manage 11.4 K 2008-08-19 - 11:47 JaimeSaiz Hipe-like error dialog
PNGpng hipe-warning.png r1 manage 13.0 K 2008-08-19 - 11:48 JaimeSaiz Hipe-like warning dialog
PNGpng java-confirmation.png r1 manage 10.8 K 2008-08-19 - 11:48 JaimeSaiz Java-like confirmation dialog
PNGpng java-error.png r1 manage 13.2 K 2008-08-19 - 11:48 JaimeSaiz Java-like error dialog
PNGpng java-information.png r1 manage 16.0 K 2008-08-19 - 11:49 JaimeSaiz Java-like information dialog
Edit | Attach | Watch | Print version | History: r39 < r38 < r37 < r36 < r35 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r39 - 2014-02-20 - AlvarGarcia
 
This site is powered by the TWiki collaboration platform Powered by Perl