Skip to content

Creating extensions

pete edited this page Oct 4, 2016 · 1 revision

Introducing QuPath extensions

QuPath extensions provide a way to add extra functionality to the main QuPath GUI without having to modify the existing code.

Extensions are written in Java and distributed as Jar files. Unlike scripts, extensions can be used to manipulate QuPath in almost any way - including replacing or removing some of the built-in tools or functionality. As such, extensions can achieve a lot more than scripts... but are a bit more complicated to create.

How extensions work

Extensions are simply Jar files that sit somewhere on QuPath's classpath - generally in a extensions directory (for an optional extension), or along with the other main QuPath application files (for a built-in extension, which is part of the 'standard' QuPath distribution).

If you are familiar with ImageJ, extensions are a bit like plugins - but not quite the same. While plugins are generally used to add one or more processing commands within ImageJ, an extension can transform QuPath in a much more flexible way.

During startup, QuPath will check its classpath to find out what extensions are available - and then install each of them in turn.

The order in which extensions are installed is undefined (i.e. best to consider it random), but it is assured that each extension will only be installed once.

Installing extensions

To install an extension, a user generally only needs to do two things:

  1. Drag one or more Jar files containing the extension onto QuPath
  2. Create a extensions directory if one does not already exist, or accept the default

QuPath will then proceed to copy the Jars into the directory, and try to install the extension immediately. QuPath will also automatically install the extension - along with any others in the extensions directory - the next time it is started up.

The extensions directory can be changed later inside the Preferences panel.

Removing extensions

Removing an extension simply involves going to the extensions directory and removing the related Jar files. It is necessary to restart QuPath for this to take effect.

Creating an extension

Creating a new QuPath extension involves three main steps:

  1. Write a Java class that implements the interface qupath.lib.gui.extensions.QuPathExtension
  2. Create a companion text file META-INF/services/qupath.lib.gui.extensions.QuPathExtension with a single line of contents that gives the full classname of the class created in Step 1.
  3. Package both the class at the text file into the same Jar.

We will not describe steps 2 and 3 in detail here. If they seem odd, then it may help to read around Services and Service Providers in Java here, since this is what QuPath extensions are based on.

However, the easiest way to get started is to copy and modify an existing extension that is available. The primary task is then to implement the QuPathExtension interface. Fortunately, this is rather straightforward:

package qupath.lib.gui.extensions;

import qupath.lib.gui.QuPathGUI;

/**
 * Simple interface for QuPath extensions.
 * 
 * This allows dynamic discovery of new extensions.
 * 
 * @author Pete Bankhead
 *
 */
public interface QuPathExtension {
	
	/**
	 * Install the extension for a QuPathGUI instance.
	 * 
	 * This generally involves adding new commands to appropriate menus.
	 * 
	 * (Where multiple extensions are present, the order in which they will be installed is undefined.)
	 * 
	 * @param qupath
	 */
	public void installExtension(QuPathGUI qupath);
	
	/**
	 * A readable name for the extension.
	 * 
	 * @return
	 */
	public String getName();
	
	/**
	 * A short description of the extension for displaying in the main GUI.
	 * 
	 * This could also contain licensing information.
	 * 
	 * @return
	 */
	public String getDescription();

}

The name and description can be almost anything - they are simply used for display. The installExtension(QuPathGUI qupath) method is the important one, since this is what allows the extension to make changes - such as adding new commands. This method is called once by QuPath.

An example of how simple an implementation of this interface may be is shown below. It shows how the RichScriptEditorExtension replaces QuPath's default (bland, black-and-white) script editor with something more colorful:

package qupath.lib.scripting;

import qupath.lib.gui.QuPathGUI;
import qupath.lib.gui.extensions.QuPathExtension;

/**
 * QuPath extension to add a more attractive script editor with syntax highlighting, 
 * making use of RichTextFX, Copyright (c) 2013-2014, Tomas Mikula (BSD 2-clause license).
 * 
 * @author Pete Bankhead
 *
 */
public class RichScriptEditorExtension implements QuPathExtension {

	@Override
	public void installExtension(QuPathGUI qupath) {
		qupath.setScriptEditor(new RichScriptEditor(qupath));
	}

	@Override
	public String getName() {
		return "Rich script editor extension";
	}

	@Override
	public String getDescription() {
		return "Adds a more attractive script editor with syntax highlighting, making use of RichTextFX - https://github.com/TomasMikula/RichTextFX";
	}

}

Admittedly, the extension does require a separate class to actually implement RichScriptEditor, and a Maven project to manage the dependency on RichTextFX. Nevertheless, once these have been written then getting them into QuPath is quite straightforward: simply put both the Jar containing the extension and any dependencies (also Jars) into QuPath's extensions folder, and the next time QuPath is started they should be automatically discovered and installed.

A slightly different extension

The above extension replaces QuPath's default script editor using a specific method for this purpose. Supposing that this wasn't the desired effect, and rather the new script editor should have its own menu item (so that either editor could be opened), then the installExtension method might be modified as follows.

    @Override
	public void installExtension(QuPathGUI qupath) {
		
		// Get a reference to a menu, creating it if necessary
		Menu menu = qupath.getMenu("Automate>Rich script editor", true);
		
		// Create a new MenuItem, which shows a new script when selected
		MenuItem item = new MenuItem("Show Rich script editor");
		item.setOnAction(e -> {
			new RichScriptEditor(qupath).showNewScript();
		});
		
		// Add to the menu
		menu.getItems().add(item);
		
	}

This alternative would add a new command to QuPath's menus that launches the new script editor. It takes advantage of a getMenu(String name) helper method to request a particular menu (or submenu, through the use of >), so that it may be added wherever is appropriate.

Extension best practices

The extension API is open to change and improvement to meet new needs, making 'best practice' hard to define at this time. However, one thing that is good to keep in mind is that it helps to have version information in the Jar Manifest file.

If using Maven for dependency management, this can be achieved by including the following in the POM file for the extension:

<!-- Store the version information in the Manifest -->
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-jar-plugin</artifactId>
	<version>3.0.2</version>
	<configuration>
		<archive>
			<manifest>
				<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
				<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
			</manifest>
		</archive>
	</configuration>
</plugin>

In this case, QuPath is able to show the version number defined in the POM under the Help → Installed extensions menu.

Clone this wiki locally