Setting persistence for parameters of GenericDialog in Jython

jython
scijava
imagej
scripting
parameters

#1

The scripting parameters with @ notation have this nice feature of using persistence between runs of the same script.
However for some applications, I have to use the Generic Dialog or Generic Dialog Plus classes instead of those @Parameters.
Unfortunately they do not offer such persistence by default. I found out that it should be possible to implement it in Jython using the PrefService interface of SciJava.
If someone would be able to provide an example that would be great !


#2

My current solution consists in storing the inputs in successive lines of a temporary file that is read and overwritten for every new run.


#3

Hi @LThomas,

nice to see you around here! :wink:

Is that the piece of code you are looking for?

#@PrefService prefs

i = prefs.getInt("counter", 0);
i = i + 1;
prefs.put("counter", i);

print(str(i));

Greetings from the MPI-CBG! :wink:

Cheers,
Robert


#4

Amazing, Thanks !

I was not aware of this #@ notation for imports ? is it specific from those Interface object ?

See you later this year here in Heidelberg for the ImageJ conference maybe ? :wink:
Salutations to Benoit if he is around !


#5

That’s actually not an import (i.e. prefs.getInt and prefs.put are not static methods), but just another regular script parameter :slight_smile:

The ImageJ2 interface is fundamentally service-driven, and you can retrieve any Service instance via script parameters that will be automatically filled during pre-processing of your script. Some examples:

#@ PrefService prefs
#@ OpService ops
#@ UIService ui
#@ DatasetService ds

These are all auto-resolved at runtime of your script.


Other parameters, such as images or numbers, can be automatically resolved in some situations, but not in others.

For example, a single image input will automatically point to the currently active image:

#@ ImagePlus imp

… whereas a drop-down choice will be displayed in an input dialog as soon as multiple image inputs are defined:

#@ ImagePlus imp1
#@ ImagePlus imp2

#6

Hey @imagejan,

btw., is there a complete list of services and types which can be discovered the #@ way?

E.g. the PrefService was new to me until @LThomas pointed at it.

Thanks!
Robert


#7

ImageJ is extensible, so having a complete list of discoverable services at compile time is impossible.


At runtime however, you should be able to get such a list via scripting, for example:

#@ PluginService plugins

import org.scijava.service.Service

plugins.getPluginsOfType(Service.class).forEach {
	println it
}

or:

#@ ImageJ ij

ij.context().getServiceIndex().forEach {
	println it
}

Note that several distinct services can implement the same interface. When asking for a specific interface, the matching service with the highest priority is returned (which allows you to provide your own service implementation for any of those if you like).

See also the Under the Hood - SciJava tutorial notebook.


The Preprocessor responsible for harvesting Service inputs is called ServicePreprocessor:


#8

Wow! The output of this little script is really interesting! Amazing, thanks!

class='org.scijava.event.DefaultEventService', priority=100000.0, enabled=true, pluginType=Service
class='net.imagej.legacy.LegacyConsoleService', priority=100.0, enabled=true, pluginType=Service
class='net.imagej.legacy.display.LegacyImageDisplayService', priority=100.0, enabled=true, pluginType=Service
class='net.imagej.lut.DefaultLUTService', priority=100.0, enabled=true, pluginType=Service
class='org.scijava.script.DefaultScriptService', priority=100.0, enabled=true, pluginType=Service
class='net.imagej.legacy.LegacyService', priority=1.0, enabled=true, pluginType=Service
class='fiji.DefaultFijiService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.DefaultMetadataService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.codec.DefaultCodecService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.formats.qt.DefaultQTJavaService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.formats.tiff.DefaultTiffService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.gui.DefaultGUIService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.img.DefaultImgUtilityService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.img.converters.DefaultPlaneConverterService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.io.DefaultNIOService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.ome.services.DefaultOMEMetadataService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.ome.services.DefaultOMEXMLService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.refs.DefaultRefManagerService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.DefaultDatasetIOService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.DefaultFilePatternService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.DefaultFormatService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.DefaultLocationService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.DefaultTranslatorService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.JAIIIOServiceImpl', priority=0.0, enabled=true, pluginType=Service
class='io.scif.xml.DefaultXMLService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.DefaultDatasetService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.DefaultImgPlusService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.animation.DefaultAnimationService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.autoscale.DefaultAutoscaleService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.display.DefaultImageDisplayService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.display.DefaultOverlayService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.display.DefaultWindowService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.display.DefaultZoomService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.measure.DefaultMeasurementService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.measure.DefaultStatisticsService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.notebook.DefaultNotebookService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.operator.DefaultCalculatorService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ops.DefaultNamespaceService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ops.DefaultOpMatchingService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ops.DefaultOpService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.plugins.commands.restructure.SplitChannelsContextMonitor', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.roi.DefaultROIService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.sampler.DefaultSamplerService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.table.DefaultTableService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.threshold.DefaultThresholdService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.types.DefaultDataTypeService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ui.DefaultImageJUIService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ui.awt.AWTRenderingService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ui.awt.AWTScreenCaptureService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ui.swing.ops.DefaultOpFinderService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.ui.swing.overlay.JHotDrawService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.units.DefaultUnitService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.updater.DefaultUpdateService', priority=0.0, enabled=true, pluginType=Service
class='net.imagej.updater.DefaultUploaderService', priority=0.0, enabled=true, pluginType=Service
class='net.imglib2.meta.units.DefaultUnitService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.app.DefaultAppService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.app.DefaultStatusService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.command.DefaultCommandService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.console.DefaultConsoleService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.convert.DefaultConvertService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.display.DefaultDisplayService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.download.DefaultDownloadService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.event.DefaultEventHistory', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.input.DefaultInputService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.io.DefaultIOService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.io.DefaultRecentFileService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.io.handle.DefaultDataHandleService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.io.location.DefaultLocationService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.io.nio.DefaultNIOService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.main.DefaultMainService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.menu.DefaultMenuService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.module.DefaultModuleService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.object.DefaultObjectService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.options.DefaultOptionsService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.parse.DefaultParseService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.platform.DefaultPlatformService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.plugin.DefaultPluginService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.prefs.DefaultPrefService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.run.DefaultRunService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.script.DefaultScriptHeaderService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.script.process.DefaultScriptProcessorService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.search.DefaultSearchService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.startup.DefaultStartupService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.task.DefaultTaskService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.text.DefaultTextService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.thread.DefaultThreadService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.tool.DefaultToolService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.ui.DefaultUIService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.ui.dnd.DefaultDragAndDropService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.ui.swing.SwingIconService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.ui.swing.script.DefaultLanguageSupportService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.welcome.DefaultWelcomeService', priority=0.0, enabled=true, pluginType=Service
class='org.scijava.widget.DefaultWidgetService', priority=0.0, enabled=true, pluginType=Service
class='sc.fiji.compat.DefaultFijiService', priority=0.0, enabled=true, pluginType=Service
class='io.scif.services.DefaultInitializeService', priority=-100.0, enabled=true, pluginType=Service
class='net.imagej.display.DummyScreenCaptureService', priority=-100.0, enabled=true, pluginType=Service
class='net.imagej.render.DummyRenderingService', priority=-100.0, enabled=true, pluginType=Service
class='org.scijava.log.StderrLogService', priority=-100.0, enabled=true, pluginType=Service
class='org.scijava.platform.DefaultAppEventService', priority=-100.0, enabled=true, pluginType=Service
class='org.scijava.cache.DefaultCacheService', priority=-10000.0, enabled=true, pluginType=Service

#9

It seems that retrieving services for compiled scripts does not work.

  • Compiled_Test.py (to put in jars/Lib)
#@PrefService prefs
MyPref = prefs  # This already fails at import

def Run():
	A = MyPref.getInt("counter",1)
	print A
  • Calling_Test.py
from Compiled_Test import Run
-> NameError: name 'prefs' is not defined

I actually need this persistence for such a compiled script, that is also generating a GUI to ask the user for some input.


#10

For some reason I don’t manage to have it working with Boolean (while it works for the other fields).

#@PrefService prefs
from ij.gui         import GenericDialog 
from java.lang      import Boolean, Class # testing with type from java

# Recover from persistence

# Boolean
#Choice0 = prefs.getBoolean("MyChoice",False) # deprecated according to doc
Choice0 = prefs.getBoolean(bool,"MyChoice",False)
#Choice0 = prefs.getBoolean(type(bool),"MyChoice",False)

# String
Text0 = prefs.get("Example","")



# Input GUI window  
Win = GenericDialog("Test")

Win.addCheckbox("MyChoice",Choice0)
Win.addStringField("MyText",Text0)

Win.showDialog()



# Recover choice
if (Win.wasOKed()): 
	
	# Boolean 
	Choice = Win.getNextBoolean()
	print Choice
	print type(Choice)

	# String
	Text = Win.getNextString()


	
	# Save input into memory/persistence
	
	#prefs.put("MyChoice",Choice) # deprecated according to doc
	prefs.put(bool,"MyChoice",Choice)
	#prefs.put(Class(Boolean),"MyChoice",Choice)

	prefs.put("Example",Text)
	```

#11

Script parameters are resolved by the SciJava ScriptService when pre-processing a script, i.e. before it is actually interpreted by any script language processor.

So for pre-compiled Python modules, script parameter will never work.

There’s some ongoing work of making cross-import of SciJava scripts (independent of the script language) work:

… however, this will likely still take some time until it will work flawless for use cases like yours.


For now, I’d recommend to use scriptService.run() or moduleService.run() to call your script from inside another script. This way, you can take advantage of all parameter processing and don’t need to fiddle around with custom dialogs and the PrefService.


#12

Very interesting finding!

I set up a minimal example to test this:

#@ PrefService prefs

retrievedValue = prefs.getBoolean(None, "persistedBool", False)
retrievedString = prefs.get(None, "persistedString", "abcdefghijklmnopqrstuvwxyz")

print retrievedValue # always prints False
print retrievedString

prefs.put(None, "persistedBool", not retrievedValue)
prefs.put(None, "persistedString", retrievedString[0:-1])

The issue seems to be how Jython booleans (True/False) are handled within Java.

I suppose that the wrong (overloaded) method signature for prefs.put is matched: instead of put(Class<?> c, String name, boolean value), the int version put(Class<?> c, String name, int value) is chosen.

This can be confirmed by changing getBoolean to getInt in the example, which will indeed retrieve the correct persisted value:

#@ PrefService prefs

retrievedValue = prefs.getInt(None, "persistedBool", False) # <-- see change here
retrievedString = prefs.get(None, "persistedString", "abcdefghijklmnopqrstuvwxyz")

print retrievedValue # now it works as expected
print retrievedString

prefs.put(None, "persistedBool", not retrievedValue)
prefs.put(None, "persistedString", retrievedString[0:-1])

For reference, the same example translated to Groovy works as expected with getBoolean:

#@ PrefService prefs

retrievedValue = prefs.getBoolean(null, "persistedBool", false)
retrievedString = prefs.get(null, "persistedString", "abcdefghijklmnopqrstuvwxyz")

println retrievedValue
println retrievedString

prefs.put(null, "persistedBool", !retrievedValue)
prefs.put(null, "persistedString", retrievedString[0..-2])

Another argument for me to recommend Groovy as default scripting language with Fiji/ImageJ :slight_smile:


#13

I forgot to mention that the Class argument on the methods in PrefService is used to associate the persisted value with a given (plugin) class, and has nothing to do with defining how (in what type) the value is stored.

When using null (or None in Python) as first argument, DefaultPrefService will implicitly associate the persisted key to the PrefService class, see here:


#14

Nice guess !
I have reported the boolean problem as an issue on GitHub.

scriptService.run() or moduleService.run()are good to know but I use the second script to store some functions that I am calling in the mainscript so I am not literally “running it” .
Additionnally the custom dialogs especially the GenericDialogPlus class from Fiji allows to have some extra functionnalities (buttons…) compared to the #@ script parameters


#15

Following a parallel post, I came up with this solution to use the PrefService in compiled scripts.

  • The compiled script PrefTest.py to put in Jars/Lib
from net.imagej.legacy  import IJ1Helper
from org.scijava.prefs  import PrefService 

context = IJ1Helper.getLegacyContext()
Pref    = context.getService(PrefService)

i = Pref.getInt("counter", 0)
i = i + 1
Pref.put("counter", i)

A second dummy script that imports the previous one

from PrefTest import i
print i # increases for every new run as expected