IJ1 or IJ2 style for plugin development?

I am starting the development of a Fiji plugin. Which plugin style should I use ?

More precisely is that possible to use public class MyPlugin implements Command instead of public class MyPlugin implements PlugIn with the IJ1 interface (because it’s for now the default interface).

I am currently using public class MyPlugin implements PlugIn with the run() method containing :

final Context context = (Context) IJ.runPlugIn("org.scijava.Context", "");
final ImageJ ij = new ImageJ(context);

But when I use ij.ui().show("test", dataset);, the image open within the IJ2 style while the main window is IJ1 style (the IJ2 window is bugged, for example I can’t even zoom on the image or scale the intensity).

Last thing : what should I use in the main() method ? ij.ImageJ imageJ = new ij.ImageJ(); or net.imagej.ImageJ imageJ = new net.imagej.ImageJ(); ?

1 Like

I would recommend you use the SciJava Command. You can still “mix and match” with ImageJ 1.x—i.e., use ij.ImagePlus etc.—while benefiting from the improvements the SciJava framework brings.

Absolutely. ImageJ2 ships several such commands already. You can actually see them using the Command Finder: look for commands whose Class column is prefixed with command:.

Furthermore, if you use Command along with @Parameter fields for your inputs and outputs, it will automatically be macro recordable, without the need for GenericDialog and similar code. See the simple-commands tutorial for examples.

Presumably you mean the run(String arg) method?

That is odd—it is supposed to open the image in an ImageJ 1.x ImageWindow as normal for that UI. Can you post a simple example illustrating the problem?

Regardless of the style you choose for your plugins, you should always use new net.imagej.ImageJ(); and not new ij.ImageJ(). This is because spinning up an ImageJ2 context does a lot more than just initialize ImageJ1. In particular, it needs to patch the ImageJ1 classes via the LegacyService.

On the other hand, the main method is only for testing, so what you put in there will not matter much in production for people calling your plugin from within ImageJ via its normal mechanisms.

1 Like

Thank you @ctrueden for your answer.

So I converted my plugin to the IJ2 style and it works fine when I run Fiji manually and copying the plugin to the Fiji dir. The plugin indeed open a IJ1 window.

Now when I run the MyPlugin.main() method within Netbeans (for testing purpose) IJ2 is loaded. How can I ask for IJ1 to load. Since it’s default for now it makes more sense to develop under the IJ1 interface I guess.

For the record and because it can help others, here is a minimal plugin :

import io.scif.services.DatasetIOService;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.imagej.Dataset;
import net.imagej.ImageJ;

import org.scijava.app.StatusService;
import org.scijava.command.Command;
import org.scijava.log.LogService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.ui.UIService;

@Plugin(type = Command.class, menuPath = "Plugins>MyPlugin")
public class MyPlugin implements Command {

    @Parameter
    private LogService log;

    @Parameter
    private StatusService statusService;

    @Parameter
    private DatasetIOService datasetIOService;
    
    @Parameter
    private UIService ui;

    @Override
    //public void run(String args) {
    public void run() {
        try {
            Dataset dataset = datasetIOService.open("/home/hadim/Documents/Postdoc/data/test/mt.ome.tif");
            ui.show(dataset);
            log.info("Load test dataset");
        } catch (IOException ex) {
            Logger.getLogger(MyPlugin.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(final String... args) throws Exception {
        // Launch ImageJ as usual.
        final ImageJ ij = net.imagej.Main.launch(args);

        // Launch the command.
        ij.command().run(MyPlugin.class, true);
    }
}

Last thing I forgot to ask : which strategy should I use to use services in the other classes of the plugin ? Should I propagate them from MyPlugin or is there is a way to call a service from inside a class.

Aha, I understand now.

Add the following dependency to your POM:

<dependency>
  <groupId>net.imagej</groupId>
  <artifactId>imagej-legacy</artifactId>
  <scope>runtime</scope>
</dependency>

That will enable the ImageJ 1.x UI, instead of the ImageJ2 Swing UI which is otherwise the default.

For any class e.g. MyHelperClass, you can add fields like:

@Parameter
private UIService uiService;

And then make a constructor:

public MyHelperClass(final Context context) {
   context.inject(this);
}

And when you create a new instance of MyHelperClass you will pass the SciJava Context as an argument to the constructor. The context.inject(Object) call will populate the @Parameter fields with that context’s associated Service objects. This is known as constructor-based dependency injection.

There are other ways to do it—the only thing that matters is that you pass the needed Context and/or Service instance along for the ride somehow to all helper objects. I like the above method because it is relatively succinct and consistent with other @Parameter service fields in SciJava plugins in general.

3 Likes

Thanks for the details !