Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using swing UI causes command dialogs to show some invisible items #18

Open
lnyng opened this issue Nov 13, 2015 · 5 comments
Open

Using swing UI causes command dialogs to show some invisible items #18

lnyng opened this issue Nov 13, 2015 · 5 comments

Comments

@lnyng
Copy link

lnyng commented Nov 13, 2015

When ImageJ is launched with the swing as the default UI:

ImageJ ij = new ImageJ();
ij.ui().setDefaultUI(ij.ui().getUI("swing"));
ij.ui().showUI();
Dataset blobs = ij.scifio().datasetIO().open("http://imagej.net/images/blobs.gif");
ij.ui().show(blobs);

Any following calls to commands that have a ImageDisplay parameter with type = ItemIO.BOTH or type = ItemIO.INPUT will cause the command dialog to show an item "display" with a long string of its ImageDisplay.toString().

For example, ij.command().run(SubtractFromDataValues.class, true); pops up such dialog with unexpected behavior. Using the legacy UI (e.g. removing the "setDefaultUI" line of code above) does not produce such problem, so I suspect that it is the swing UI causes the problem.

Is this a bug, or is there any correct way to programmatically run a command so that the display item will be invisible? Thanks!

@lnyng
Copy link
Author

lnyng commented Nov 14, 2015

I think the problem can be pinpointed to the DefaultDisplayService. When I set a breakpoint at the 114 line, the problem does not appear; but if I set a breakpoint at the 117 line, the problem appears, and the conditional breakpoint shows that the displayList was empty at the moment the breakpoint is hit, but immediately after that the list is added with one display object by another thread.

Therefore, it seems that the display-activation event is published too slowly for the swing UI when a command is called programmatically, so that the display input is not resolved at that moment. @ctrueden any reason this would happen?

@lnyng
Copy link
Author

lnyng commented Nov 14, 2015

After playing around with the Eclipse debugger for a while, I think I figure out how most of this process works and where the problem lies.

DefaultUIService#onEvent is invoked due to a DisplayCreatedEvent, which consequently schedule a runnable that will invoke the view(w, d) method of a corresponding DisplayViewer.

In the case of opening an image, the legacy mode uses LegacyImageDisplayViewer, which sets the active display immediately in the view(w, d) call; in contrast, the corresponding DisplayViewer does not set the active display until a display window is constructed, and then the DefaultDisplayService#onEvent finally sets the active display.

In the meanwhile, the main thread is running to preprocess the command, which looks for a suitable active display to inject into the command.

I found that the call stack of constructing a swing window is quite large, so that the main thread always asks for active display before the WinActivatedEvent can invoke the setActiveDisplay method. Since the legacy mode sets the active display before constructing the window, there is no such problem in legacy mode.

If this is actually the problem, then adding a line of code that sets the active display in the view(w, d) will be a hacky solution, but should we also suppose that a display should not be not ready until its window is ready? Then we might need to let other threads wait until the display is active. @ctrueden What do you think?

@lnyng
Copy link
Author

lnyng commented Nov 16, 2015

For now, this could be a workaround:

ImageJ ij = new ImageJ();
ij.ui().setDefaultUI(ij.ui().getUI("swing"));
ij.ui().showUI();
Dataset blobs = ij.scifio().datasetIO().open("http://imagej.net/images/blobs.gif");
Display<?> dsp = ij.display().createDisplay(blobs);
while (ij.display().getActiveDisplay(dsp.getClass()) == null) {
    Thread.sleep(10);
}
ij.command().run(SubtractFromDataValues.class, true);

But the call to getActiveDisplay might cause ConcurrentModificationException if sleep with smaller numbers, and it looks better if createDisplay() can handle this internally.

@lnyng
Copy link
Author

lnyng commented Nov 16, 2015

Using @EventHandler also works, but not quite good looking:

ImageJ ij = new ImageJ();
ij.ui().setDefaultUI(ij.ui().getUI("swing"));
ij.ui().showUI();
Dataset blobs = ij.scifio().datasetIO().open("http://imagej.net/images/blobs.gif");
Display<?> dsp = ij.display().createDisplay(blobs);

final Collection<EventSubscriber<?>> subscribers = new ArrayList<EventSubscriber<?>>();
Object obj = new Object() {
    @EventHandler
    private void onEvent(WinActivatedEvent event) {
        if (event.getDisplay().equals(dsp)) {
            ij.command().run(SubtractFromDataValues.class, true);
            ij.event().unsubscribe(subscribers);
        }
    }
};
subscribers.addAll(ij.event().subscribe(obj));

@ctrueden
Copy link
Member

Thanks for the thorough followup, Leon. I'm definitely not a fan of any sleep-based workarounds. IJ1 is full of those and it causes nothing but trouble in the long run. We can make things concurrency safe, I'm sure.

lnyng pushed a commit to imagej/imagej-ui-swing that referenced this issue Nov 18, 2015
Racing condition occurs when a command is programmatically invoked
immediately after a display is created, using swing ui. The racing
condition is resovled in a similar way to the LegacyImageDisplayViewer.

See scijava/scijava-ui-swing#18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants