ImageJ2 Command - callback function for persisted Parameters

parameters
Tags: #<Tag:0x00007fd543019be8>

#1

Hi everyone!

I have a question regarding ImageJ2 / SciJava Command Parameters. They can be used to build the dialog of a plugin. If the attribute persist is true, a Parameter will save the value that was entered the last time. I am looking for a way to trigger a function after this remembered value is set. The function attached to initializer does trigger after initialization but before setting the saved value, callback does only trigger if the value is changed again via the user interface.

Is this possible somehow?

Thanks,
Deborah


#2

Dear @frauzufall,

Currently not but that behavior will likely change with SciJava 3:

https://github.com/scijava/scijava-common/pull/236


What exactly are you trying to achieve? Will the callback function set the @Parameter based on the loaded value or are you looking for an initializer that just overwrites the loaded value regardless of the content?

Best,
Stefan


#3

Thanks for your answer!

I use a File Parameter and another String Parameter to display information about that file. I can do that using the callback and update the String there if the user changes the file, but not if one restarts the program and the picked file is remembered. I cannot update the String when the Dialog is opened again, because initializer is called before the remembered file is loaded.


#4

Wouldn’t it be possible to also persist the String? That way, the user will start with the File and String from the last execution. Once, a new file is selected you are using the callback to set the String. Does that make sense? Maybe I am missing something…


#5

Yes, that makes sense. But if the file got changed or deleted in between, the status String will be incorrect. As a workaround I have a button that updates the information.

I also wanted to add dynamic content to the dialog (like a drop down list that is fed by content from the file). This is impossible without a function that can be called after the file is set. I create an additional swing dialog for this functionality. I guess the Parameter based GUI is not meant to be too complex or dymanic? Or can an InteractiveCommand do something like this?


#6

Hmm. Maybe this is a good time to ping @ctrueden and see what he has to say (I’d also be interested in his opinion).

I think the Interactive in the name is more about interactivity with respect to changes in an image: think about previewing settings of the command on a portion of the currently active image.


#7

The main point of InteractiveCommand is to mark that you want the dialog box to be non-modal. For convenience, it implements Previewable and Interactive, and calls run() every time any parameter value changes, although this behavior can be overridden.

Certainly we want the parameter-based GUI to be both powerful and convenient. However, the primary requirement is that it be agnostic to the UI. It is declarative, and up to each application (e.g., ImageJ, KNIME, CellProfiler, OMERO, headless CLI) to decide exactly how to harvest the parameters. I am reluctant to add very fancy things which require complex logic in the scijava-ui-swing component, because not all applications leverage that component. When developers inevitably lean on those fancy things in clever ways, their commands will de facto work only in ImageJ, and not in those other paradigms. I very much want to discourage this situation from happening.

On the other hand, I do also want to make it possible to craft nice dialogs, comparable at least to the ImageJ 1.x GenericDialog et. al. Otherwise, everyone will just code their own Swing dialog (or stick with the IJ1 stuff), and then their commands still won’t be usable across the various paradigms. It is a tough balancing act.

We could trigger callbacks when values are initially set during preprocessing, although we may want to be careful with this. It seems to me that in your case, the correct/intuitive behavior is indeed to respond to a callback on the File parameter, updating the matching String. No?


#8

Thank you for your reply! I absolutely want to support the parameter based UI that can be built by different applications and I feel the difficulty of this process.

I have to use Swing at the moment, because with the parameters I cannot create dynamic dropdowns, the choices have to be declared via annotation but I want to load them from an array at runtime. Have I overlooked a way to edit them?

The callback problem is just that there is no function that gets triggered once a parameter is set to a remembered value.

  1. Parameter initialized -> triggers initializer
  2. If persisted, parameter is set to remembered value -> no trigger
  3. Parameter changed -> triggers callback

#9

There is a way. But it only works for Java plugins at the moment (not from scripts). I added a dynamic-commands branch to imagej/tutorials which illustrates this.

Note that as of now, it will work to populate a multiple choice parameter at initialization, but not to refresh the choices dynamically based on the value of another parameter changing (i.e., from callbacks). The branch I created demonstrates how you would go about doing the latter, but if you actually try to run it, you’ll see that there is no visible effect. This is because the Swing UI’s InputHarvester implementation currently does not refresh widget properties such as the choices of a JComboBox—only the current values. This could certainly be fixed, but would require some care to avoid a full layout recomputation in response to every callback invocation.

See also:

We should probably file an issue along the lines of that old imagej-devel thread.

I have tried to fix this problem on the callback-upon-load branch of scijava-common. Could you please give it a try and let me know whether that makes the situation for you?

As a short-term workaround with the current SJC, you could:

  • Set persist = false on the parameter.
  • In the initializer method, load the value yourself using the PrefService, then use it to populate the dependent field.
  • In the run method, persist the value yourself using the PrefService.

Certainly not ideal, but would get the job done till the bug-fix above is merged and released.


#10

Nice example! I understand that the layout recomputation can be tricky, but I am not sure whose issue this is. Should I open one at scijava-common?

Works like a charm! Your proposed short-term solution is also working, I did not know about the PrefService before and it’s super handy. Just posting the relevant code, may be useful to someone:

@Parameter(label = "myfile", initializer="fileInitialized", persist = false)
private File file;
	
@Parameter
private PrefService prefService;
	
public void fileInitialized(){
	String filepath = file == null ? "" : file.getAbsolutePath();
	file = new File(prefService.get("myfile", filepath));
	// ..
}

@Override
public void run() {
	prefService.put("myfile", file.getAbsolutePath());
	// ..
}

#11

The input harvester UI code in scijava-ui-swing would need to be enhanced to support smarter dynamic updates of widget attributes such as the multiple choice list. It is also possible some tweak or other might be needed in scijava-common, although I am not sure without trying.

Also, I merged the dynamic-commands branch to master; the relevant commit is imagej/tutorials@9880387c now.