A Tutorial
View the Project on GitHub haesleinhuepf/extend-macro-autocompletion
Since we introduced auto-completion in Fijis script editor, some developers asked me how they can make their tools discoverable by auto-completion. That’s a good question as the auto-completion also allows pointing users to online resources or publications to cite. This tutorial explains how to achieve this. I’m providing a fully functional example as this might be a better starting point for developers.
As a starting point we assume to have a maven project with a pom.xml file defining a SciJava plugin for Fiji.
Our project also contains a class marked as Fiji plugin, and definition of all parameters the SciJava way. If you want to learn how to make SciJava/Fiji plugins, this 5 minute video is recommended.
The actual algorithm we want to ship to users is available as a single method simplifying incorporating it in ImageJ macro and the script editor.
ImageJs macro extensions are a pretty old and powerful mechanism to extens ImageJs macro language with custom code. It can be used by defining a class implementing the interface MacroExtensions interface:
public class MyMacroExtensions implements MacroExtension {
This class needs to implement two methods: At first, the method getExtensionFunctions()
is called from the Macro Interpreter
in order to get a list of supported methods.
These methods will then be accepted by the macro interpreter in case the number and types of parameters are correct.
These parameters need to be defined in an int array an may contain entries according to this definition. I only worked with these two so far:
A valid ExtensionDescriptor could then look like this:
new ExtensionDescriptor("methodName", new int[]{MacroExtension.ARG_STRING, MacroExtension.ARG_NUMBER}, this);
The this
argument tells the Macro Interpreter which class can handle the particular macro extension.
As hardcoding this line for any method call is a bit odd, I suggest an interface all your plugins need to implement:
public interface MyMacroExtensionDescriptor {
void runFromMacro(Object[] parameters);
int[] parameterTypes();
String description();
String parameters();
}
The second responsibility of our MacroExtensions class is handling method calls. This is done by the handleExtension(String name, Object[] args)
method.
It has two parameters: The name
of the method which was called and the arguments passed as array of Objects.
Again, as it is odd to check the name
variable for each individual method names, I suggest an alternative:
the method
name shall be identical with the class name
of your plugin prefixed with a custom library identifier preventing clashes with other macro extensions.
This is no must but I believe it’s a welcome simplification.
Furthermore, the object array usually comes with items of type String
and Double
.
Knowing this allows us to cast and parse handed over parameters in our
individual plugin implementation.
Then, we can call our algorithm:
@Override
public void runFromMacro(Object[] parameters) {
// We get an array of objects from the Macro interpeter.
// We need to convert/cast it to what we need
ImagePlus imp = WindowManager.getImage((String)parameters[0]);
double scalar = (Double)parameters[1];
// call the actual algorithm
actualAlgorithm(imp, scalar);
}
Implementing getExtensionFunctions()
and handleExtension(String name, Object[] args)
is all you need to enable
ImageJs Macro Interpreter to offer your methods to end-users coding macro.
Last, but not least, you need to actually tell the Macro Interpreter that your extension exists:
// Activate this class as handler for macro extensions
Functions.registerExtensions(this);
In order to tell users via auto-completion that your methods exist and which parameters they take, you need a class implementing the MacroExtensionAutoCompletionPlugin interface. This interface allows you to ship entries of the auto-completion pulldown of type BasicCompletion to the script editor.
A valid BasicCompletion
would look be
new BasicCompletion(completionProvider, "MyLib_MyMethid", null, "This is the description.");
Again, as it’s odd to define these entries by hand, we do this via the interface introduced above.
Last but not least, in order to make all plugins discoverable, put them in a list.
// list of all available plugins
MyMacroExtensionDescriptor[] list = {
new MyPluginAdd(),
new MyPluginMultiply()
};
There is an alternative for implementing an automated discovery strategy demonstrated in the plugin zoo example code.
In order to make your plugins discoverable by ImageJs Macro Interpreter and auto completion in Fijis script editor, feel free to fork this repository, put in your algorithms (or depend on them) and implement a small class for every method you would like to offer to the users. This class should contain:
@Override
public int[] parameterTypes() {
return new int[] {MacroExtension.ARG_STRING, MacroExtension.ARG_NUMBER};
}
@Override
public String parameters() {
return "imageTitle, number";
}
@Override
public String description() {
return "Hey folks, just enter an image and a number.\n\n :-)";
}
@Override
public void runFromMacro(Object[] parameters) {
// We get an array of objects from the Macro interpeter.
// We need to convert/cast it to what we need
ImagePlus imp = WindowManager.getImage((String)parameters[0]);
double scalar = (Double)parameters[1];
// call the actual algorithm
actualAlgorithm(imp, scalar);
}
It’s not 100%ly trivial to implement ImageJ Macro Extensions and extending auto-completion. Thus, I hope this tutorial helps. Furthermore, it might be a starting point for a public discussion on how we as a community want to have ImageJ, Fiji and friends extensible.
I think users will thank developers if they go this step in order to simplify access to algorithms via ImageJ macro. And if developers put links to citable software in the auto-completion, the user will thank by citing the software properly. I’m sure!
As always: Feedback is highly appreciated. Either via twitter or old-school: rhaase at mpi minus cbg dot de.
Happy coding!
Cheers,
Robert @haesleinhuepf Haase