Register Virtual Stack Slices Plugin hangs on MacOS

Hi guys,

I just had an issue with the "Register Virtual Stack Slices Plugin in FIJI: Whole FIJI freezes immediately after clicking OK in the inital Dialog. The bug is hard to reproduce, because it does only happen in 20% of the times I tried. However, after excessive debugging, I figured out, that it has something to do with JFileChooser.showOpenDialog calls in the run function of the Register_Virtual_Stack_MT class. Thus, the bug only happens if I turn “Save transforms” checkbox on or the “Shrinkage constrain” checkbox off.

The issue with JFileChooser is already known for some time:

And a potential solution (using FileDialog instead JFileChooser in case preferences say so) was written by @tpietzsch in

I wonder now, if anyone has already experience with this issue and how solving it would be efficient. As the bug in JFileChooser wasn’t solved in the past two years, wouldn’t it make sense to takle this in ImageJ/FIJI? E.g. by introducing a getUserDefinedFileLocation() function somewhere in FIJI which does the MacOs-detection and opens FileDialog instead of JFileChooser in case it runs on a Mac? I mean, I can copy the code from Tobias and file a Pull Request to the Register Virtual Slices Plugin. But I’m really not a big fan of copying code from one plugin to another and another and another… :wink: and another.

What do you (ImageJ/FIJI developers) think about that?

Cheers,
Robert

Ha. I didn’t know about the JFileChooser issues. It never happened to me.

It’s a pity, because FileDialog is also not without problems. On some versions of OS X (I forgot the details), it fails silently and undetectably if a FilenameFilter is used. (To the caller it looks like the FileDialog was canceled, when in fact it is never shown). Hence the crazy workedWithFilenameFilter workaround in http://github.com/bigdataviewer/bigdataviewer_fiji/blob/master/src/main/java/bdv/ij/BigDataViewerPlugIn.java#L67-L100

I always though JFileChooser would be the safer option. Apparently not. OS X is such a mess… :frowning:

Anyway: I think it is a good idea to honor the JFileChooser vs FileDialog made in ij.Prefs.

I didn’t know about it. I will add the preference check as @tpietzsch did as soon as I can.

@haesleinhuepf just out of curiosity, what version of java are you using?

My FIJI is ImageJ 2.0.0-rc-46/1.50b; Java 1.8…0_66 and I do have JDKs installed. By the way: MacOSX 10.10.5

But before you start implementing this, let’s discuss if there isn’t an ImageJ- FIJI-wide solution for that… The tiny software engineer in my head says that it’s wrong to have several copies of the same code everywhere distributed in FIJI…

And I’m furthermore not sure if this solution works… I mean: who sets the ij.Prefs option and when?

Use the chooseFile methods of UIService. Then you do not need to write any additional code; it will automatically respect the “Use JFileChooser” option of ImageJ1.

4 Likes

Thanks for the hint @ctrueden! Can you point me out to an example of how to use it? Should I use DefaultUIService?

That’s even more than I expected! Cool! Thanks @ctrueden ! I will use this in my tools as well :sunny:

Sorry @iarganda, was on vacation. There are indeed many examples; see e.g. the LoadAndDisplayDataset tutorial, which calls the UIService through an ImageJ gateway. Alternately, you can write an ImageJ2 command:

@Plugin(type = Command.class)
public class Hello implements Command {
  @Parameter
  private UIService uiService;

  @Override
  public void run() {
    File f = uiService.chooseFile(null, FileWidget.OPEN_STYLE);
    uiService.showDialog("You chose: " + f);
  }
}
1 Like

@ctrueden That’s cool! Is there (or is there planned) a chooseFile variant with a FileFilter/FilenameFilter?

Good idea. Does not exist yet, but would be very easy to add. Feel free if you need it. Or file an issue otherwise.

@ctrueden I just wanted to start playing with it…
I tried

Context context = new Context( UIService.class );
UIService service = context.getService( UIService.class );
File file = service.chooseFile( null, "open" );

but this doesn’t work. If I understand correctly, it wants a UserInterface and there is only AbstractUserInterface in scijava-common. So it seems like it’s not usable without ImageJ? Or is there anything less heavy than pulling up a ImageJ instance to get to this chooseFile?

If you are executing your code within ImageJ, then an ImageJ instance already exists. Spinning up another one is unnecessary—and in some cases harmful, due to how the LegacyService works.

There are multiple ways to get a handle on the existing Context and/or its Services:

  1. Use the Framework: Execute your code in a way that injects the needed Context and/or Services. The example I posted above does that, because the code is a SciJava Command which is invoked by the framework, which knows the current Context.

  2. Perform Dependency Injection: Inject the Context yourself from your calling code. This only works if you control the calling code. For example, if you have a Command, which wants to create a new helper class DialogShower, then you simply pass along the known Context to that DialogShower. This is dependency injection in a nutshell. You can do it via the constructor (i.e. make the DialogShower require a Context as argument), or you can do it via the @Parameter mechanism by calling context.inject(myDialogShower), which will populate all @Parameter fields of the myDialogShower object.

  3. The Hacky Way: If you really do not have a handle on the current Context—for example, because the code you are writing is part of an ImageJ 1.x plugin (e.g., the run(String) method of an ij.plugin.PlugIn)—then you can call the following static method to obtain the application context:

    Context ctx = (Context) IJ.runPlugIn("org.scijava.Context", "");
    UIService uiService = ctx.service(UIService.class);
    

    But please note that this is an inherently limited approach which only works within the context of ImageJ1. If your code is intended to run without dependency on ImageJ 1.x, this will not work for you, and in fact will prevent use of multiple simultaneous Contexts, because it violates Context encapsulation.

Let me know if any of that does not make sense, and I can clarify further.

I presume you tried this in Eclipse with only scijava-common on the class-path. The reason it does not work then is probably because you had no actual user interface on your class-path. The UIService invokes the highest priority UserInterface plugin that it finds in your Context (which normally means on your class-path). If you did not include scijava-ui-swing or scijava-ui-awt or other UserInterface plugin, then no dialog will be shown.

Furthermore, the UI also has to be visible, I believe. So you probably need to write service.showUI() before calling chooseFile. That behavior could potentially be changed, but we need to be careful: what if multiple UI plugins are present on the class-path? Which one(s) should show the dialog then?

Yes, I’m trying this from eclipse.

Thanks for the pointers. Now I added ‘scijava-ui-swing’ (brings also ‘scijava-ui-awt’ as a dependency). With this, the above ends up calling AbstractSwingUI.chooseFile().

If I add net.imagej imagej as a dependency, and do it via

ImageJ ij = new ImageJ();
File file2 = ij.ui().chooseFile(null, "open");

I end up in AbstractSwingUI.chooseFile() too. So JFileChooser in both cases.

I couldn’t figure out where the decision according to ij.Prefs is made. Could you point me there?

1 Like

The imagej-legacy component includes a LegacyUI that extends the SwingSDIUI but also leans on ImageJ 1.x in various ways—such as for showing file dialogs. See here.

As a general rule, any time any class of ij.* is involved whatsoever, it nearly always comes down to imagej-legacy, or more rarely ij1-patcher.

2 Likes