Skip to content

09. Extending Peakaboo

Nathaniel Sherry edited this page Apr 30, 2024 · 2 revisions

Peakaboo allows users who are familiar with the Java programming language to extend its functionality by creating custom filters and adding support for new file formats. Users who are interested in developing extensions for Peakaboo will need to make sure they have the Java Development Kit (JDK) installed on their computer (available from Java.com). A dedicated code editor is also recommended.

Plugins in Peababoo are classes which implement a specific interface. The interface in question depends on the kind of plugin being created. These plugin classes should be bundled in .jar files and placed in the correct plugin folder inside of the user’s application data directory. For example, on Linux, this path is ~/.config/Peakaboo5/. You can access this folder by selecting menu Menu → Plugins → open-symbolic Open Folder.

In order to know which classes to load without scanning each class in every jar file, a jar containing a plugin must include a manifest file, located at META-INF/services/ under the root of the jar file. The file name must be the qualified name of the interface that this type of plugin implements, and the contents of the file must be the qualified names of each class to be loaded as a plugin, one per line.

The specific interfaces used for different types of plugins are:

  • Data Source: org.peakaboo.dataset.source.plugin.DataSourcePlugin

  • Data Sink: org.peakaboo.dataset.sink.plugin.DataSinkPlugin

  • Filter: org.peakaboo.filter.model.Filter, although see also org.peakaboo.filter.model.AbstractFilter

  • Map Filter: org.peakaboo.mapping.filter.plugin.MapFilterPlugin

Plugins should also define a public no-argument constructor.

Example plugins for both filters and data sources are available online, along with a jar file containing Peakaboo for building against.

Filters

Parameters

Filters define a series of Parameters, each with an associated data type. These parameters are displayed in the filter’s settings panel, using the associated data types and class information to determine what kind of widgets should be used. These parameters should be created and initialized in the filter’s initialize method.

Once constructed, the parameter should be passed to the addParameter method of the superclass. This method returns an integer value which you can use to retrieve the parameter later using getParameter(int index). The alternative is to simply keep local references to all your Parameters. Here is an example of the initialize method of the Addition filter:

@Override
public void initialize()
{
  Parameter<Float> add = new Parameter<>(
    "Amount to Add", //Human-readable name
    new RealStyle(), //Style hint for UI generation
    1.0f,            //Starting value
    this::validate); //validation function
    
  addParameter(add);  
}

This creates a new Parameter called Amount to Add with a Float data type, a default value of 1.0, and a validation function defined elsewhere in the class body. It then adds it to this filter’s set of parameters.

Validation

Whenever the user makes changes to a filter’s parameter value in the settings Settings dialog, the new parameter value is validated in a call to the parameter’s validator method. In the example above, the parameter’s validate method is in the filter itself, and could be used by other parameters to validate combinations of values.

If the parameter values do not validate, then the input fields are reset to their old values.

Processing Spectra

An active filter’s filter(SpectrumView data, Optional<FilterContext> ctx) method is called repeatedly. This method should return a modified SpectrumView containing the results of filtering the input SpectrumView.

The Spectrum and SpectrumView classes have a rough List-like interface, but use a fixed-size float array to store the actual data for memory and performance reasons. If you prefer to work with the float[] data directly, the backingArray method provides direct access to it.

File Formats (Data Sources)

Adding support for new file formats in Peakaboo is accomplished by creating a new Data Source. When creating a new data source, the DataSourcePlugin interface must be implemented. The implementation should define a public no-argument constructor. A Data Source has a number of methods to retrieve other data structures, some of which are Optional.

File Format

The FileFormat interface is how a Data Source describes the type of files it reads and tests if it can read specific files. When the user opens one or more files, Peakaboo must determine which Data Source plugin(s) may be used, and it relies on the Data Source’s FileFormat to inform it.

For convenience, the SimpleFileFormat class provides an easy way to get started, with file-extension based testing and the ability to specify single or multi-file support. Because many data sources use very common file extentions (e.g. .dat) or container files (e.g. HDF), developers should consider implementing proper testing through an examination of a file’s contents.

Reading the Data

Once your data source has been selected, the void read(DataSourceContext ctx) method will be called at most once. The paths given here will have already been checked for compatibility through the supplied FileFormat, so there is no need to recheck the files.

Because Peakaboo is an interactive desktop application, progress should be reported to the user through the Interaction class. The Data Source interface defines get/set methods for this object, and an instance will be provided by Peakaboo before the data is read. It contains several methods which can be used to report progress or allow user control.

boolean checkReadAborted()
void notifyScanCount(int scanCount)
void notifyScanRead(int number)

The checkReadAborted method checks to see if the user has decided to abort the read operation. This method should be checked periodically -- perhaps once per second. If it ever returns true, the read operation should be halted. The data source instance will not be used after the user has requested the read to be aborted, so there is no need to worry about any inconsistent state caused by aborting the read. Any open files should still be closed.

The notifyScanCount method reports the number of scans in the data set to the user interface. The notifyScanRead method reports that a certain number of new scans have been read since it was last called. These two methods used together allow Peakaboo to show the progress of the read operation to the user. In some file formats, it is not feasible to determine the number of scans in advance of reading them. In this case, simply do not call either of these methods, and the interface will show a "busy" indicator instead.

Accessing the Data

Once the read method has been called, your data source will need to provide access to the data. This is achieved through the ScanData interface. For convenience, the SimpleScanData and PipelineScanData classes are provided. The former is indented for ease of use, while the latter is intended for best performance.

Providing Extended Information

Each data source reads from different kinds of files, which contain different kinds of information about the scan. Further information can be exposed through the DataSize, PhysicalSize, and Metadata interfaces, which are all access through the DataSource interface as Optional values.