In ImageJ2 commands, how to update displayed parameters?

I am developing an InteractiveCommand using ImageJ2 and SciJava.

The main command serves as a central GUI that launches other commands and gather parameters but also button for the user to show other GUI panels, where they can fine tune the value of one of the parameters with an interactive GUI.
If they are happy with the value they have been probing, the chosen value should be reported in the main GUI.

I do this via a Button with the following callback:

protected void adjustThreshold()
	{
		final AdjustThresholdDialog< T > adjustThresholdDialog = new AdjustThresholdDialog<>(
				imageDisplayService.getActiveImageDisplay(),
				circleThickness, thresholdFactor / 100.,
				opService.getContext() );
		adjustThresholdDialog.addActionListener( new ActionListener()
		{
			@Override
			public void actionPerformed( final ActionEvent e )
			{
				if ( e.getActionCommand().equals( "OK" ) )
				{
					thresholdFactor = adjustThresholdDialog.getThresholdFactor() * 100.;
					circleThickness = adjustThresholdDialog.getCircleThickness();
				}
			}
		} );
		adjustThresholdDialog.setVisible( true );
	}

Here, thresholdFactor and circleThickness are 2 fields of the main command, annotated with @Parameter.

The problem I have is that the changes done in this method are not directly shown on the GUI. The changes are displayed only the next time
the user clicks a button with a callback.

What is the proper way to trigger GUI updates of the parameter changes?

1 Like

@tinevez Are you constructing and showing your own JFrame or JDialog? If not, and you are letting the input harvester do the work, then you should not be using javax.swing or java.awt stuff in your code. Instead, write something like:

@Plugin(type = Command.class)
public class MyCommand implements InteractiveCommand {
  @Parameter(callback = "thresholdChanged")
  private double threshold;
  @Parameter
  private String message;

  private void thresholdChanged() {
    message = "Current threshold value = " + threshold;
  }

  @Override
  public void run() {
    // do stuff...
  }
}

Written quickly and untested. Is there some reason the callback feature of @Parameter does not do the job for you?

2 Likes

No no. Everything is fine from the main Command.
The issue I have is when I change the threshlold value from outside. I wish the display to be updated accordingly.

Yes, but if you use an @Parameter with callback annotation as suggested by @ctrueden, it should work, no? Why do you need your own ActionListener?

I think I need more information in order to help. Could you post your code, with instructions on how to run it, as well as a description of what you want to happen when certain actions are taken?

Ok sorry, the code is here:
https://github.com/tinevez/CircleSkinner/tree/master/src/main/java/net/imagej/circleskinner/gui.

I have a command (https://github.com/tinevez/CircleSkinner/blob/master/src/main/java/net/imagej/circleskinner/gui/CircleSkinnerCommand.java)

that has a Button (https://github.com/tinevez/CircleSkinner/blob/master/src/main/java/net/imagej/circleskinner/gui/CircleSkinnerCommand.java#L139-L142)

that launches a JDialog via a callback (https://github.com/tinevez/CircleSkinner/blob/master/src/main/java/net/imagej/circleskinner/gui/CircleSkinnerCommand.java#L353-L372).

When this dialog is closed (Oked or canceled), some values are fetched from the dialog and pushed back to the Command parameters:

				if ( e.getActionCommand().equals( "OK" ) )
				{
					thresholdFactor = adjustThresholdDialog.getThresholdFactor() * 100.;
					circleThickness = adjustThresholdDialog.getCircleThickness();
				}

with circleThickness and thresholdFactor being two parameters of the command.

Their values are changed properly, it is just that the GUI corresponding to the Command does not update these changes.

Thank you for your patience.

Thanks @tinevez. I understand the problem now.

The input harvester mechanism was not designed to be externally accessible. It is one preprocessing step of many which occur during module execution. There is no way for the command object itself (even a DynamicCommand!) to access the dialog which happens to exist at that point.

Why does there need to be a button that pops a second separate dialog? Why not simply use a slider style for both the threshold and the circle thickness, and update them dynamically whenever they change in the main dialog? I guess you would have to show the preview images constantly, then. I confess I am not sure what your intended workflow here is. Do you want changes in the main dialog to constantly trigger recomputations?

We could make a new kind of command, or perhaps modify InteractiveCommand, to offer some sort of accessor on the dialog, which would return non-null only while the dialog is visible. I have reservations about this, though—it feels like a conflation of concerns. But I suppose it is already the case that your command (or really any InteractiveCommand) cannot be used headless or from other contexts such as KNIME, so maybe it is OK. What do you think? Or this could be a new method of the WidgetService: something like WidgetModel widgetService.getVisibleWidget(Module) on which you could then call getPanel().refresh().

Thanks @ctrueden for the insights.

Yes I was hoping I could access the input harvester.

The second dialog pops up because it deals with only a fraction of the dataset (a small image within a large collection, possibly cropped).
One solution is - as you say - to put everything in one dialog and do that cleverly: A preview mechanism that would operate on a subset of the image, while run() does the real thing.

Another solution is simply to go for an explicit Swing GUI that launches non-interactive commands. But I really like the parameter persistence and GUI generation.

1 Like

Note that for InteractiveCommand, there is no difference between preview() and run(). All changes to the UI trigger run().

I am looking into adding a method WidgetService#getVisiblePanel(Module). However, we need to think carefully: is it possible a module execution could have more than one simultaneously visible panel? What if there is more than one simultaneously visible UI? What happens…?

Don’t go there.
Don’t go there.

So… you want to try solving this another way for now? OK, fine by me. If you later decide you need direct access to the InputPanel, let me know, and we can discuss further and find the best way forward.