ScriptService.run(ScriptFile,..,..) only with script parameters?

fiji
jython
macro
scripting
#1

This question is related to those previous posts:

I am also trying to call a script (subScript) from another script (mainScript).
subScript would be a local file written in any of the scripting language, and the input arguments for subScript would be passed as a string from mainScript similar to the macro recorder execution.
So basically the GUI for mainScript would look like that (in reality I will have other fields in the future for the main script)
image
FolIowing the previous post, I managed to use ScriptService.run(PathToSubScript..) using script parameters for the input of SubScript.
The problem is that this command expects the parameters to be formatted into a list style like [param1,value1,param2,val2] (in Jython at least). Which is not very user-friendly nor straightforward by programing.
It also does not work if subScript is using the GenericDialog class for the input.

I also tried to use IJ.runMacroFile(PathToSubscript, Args)
which would be convenient as the Args string can be copy/past from the macro recorder.
However I found out that the subScript must call IJ.getArgs to get the string of arguments, which means “manually” parsing the string. Not very convenient and not what most plugins do.

This solution is also very much ImageJ1 so it does not work to call some services in subScript.

Here some example with Jython scripts

  • mainScript
#@ ScriptService scriptService
#@ String any
#@ File subScript
from ij import IJ

# Option 1: runMacroFile
IJ.runMacroFile(subScript.getAbsolutePath(), "some_string=esfhseh") # With Generic dialog ->opens the GUI, with script_parameters does not get the variable

# Option 2: ScriptService
params = ["some_string", "Bla" ] # to be passed to subScript
scriptService.run(subScript, True, params) # works with script parameters only not GenericDialog
  • subScript
#@ String some_string
from ij import IJ
IJ.log(some_string)

So I am looking for a more straightforward way to call an external script with a string of arguments as returned by the macro recorder.

#2

Did you try (after putting your sub_Script.py into any subdirectory of Fiji.app/scripts/):

IJ.run("sub Script", args)

??


It should also work with a map/dict-like argument:

{ param1: value1, param2: val2 }

Edit: Sorry, I just read the linked threads, so this seems to be a limitation of the Jython script engine indeed. The solution: switch to a different language that’s closer to the JVM, such as Groovy :wink:

#3

This works probably but the idea would be that the user can provide any home-made script without a condition on the file location. I could do a copy of the script file to this folder though.

The hashmap/dictionary style would not help much as I would still need to parse the argument string to make this dictionary, which could be tricky if there are spaces in the arguments.

Instead I changed my design so that the subscript always take a single #@File input corresponding to an image path. The rest of the parameters are fixed in subscript (like a recorded command).

#4

It also works to pass a Map instead of a String... of key/value pairs.

How can we improve straightforwardness and/or user-friendliness here?

SciJava script parameters are a completely separate thing from ImageJ1 GenericDialog, yes. However, you can adapt from one to the other by writing SciJava script parameters as the script inputs, and then mashing them all together into a string and feeding it to an IJ1-style arg and invoking the IJ1-style plugin.

SciJava has a ParseService that converts from String to Map in a much more robust way than that supported by ImageJ1. But it is not syntactically identical to the ImageJ1 arg syntax.

Here is an example Jython script:

#@ ParseService parser
s = "{success=true, mode=fancy, avagadro=6.02e23, 'Hello world', 3.14159, [animal='fox']}"
result = parser.parse(s, False)
for item in result:
	name = item.name()
	value = item.value()
	print('{} = {} ({})'.format(name if name else '()', value, type(value)))

which prints:

success = 1 (<type 'bool'>)
mode = fancy (<type 'org.scijava.parse.eval.Unresolved'>)
avagadro = 6.02e+23 (<type 'float'>)
() = Hello world (<type 'unicode'>)
() = 3.14159 (<type 'float'>)
() = [animal] (<type 'java.util.Arrays$ArrayList'>)

Why do you feel you need to parse an ImageJ1 arg string into an explicit map? I feel like this is going in the wrong direction from the standpoint of correctness: ImageJ1 arg strings are limited in what they can express syntactically. You want to be able to easily copy/paste from the macro recorder? What if the macro recorder produced a better syntax in the first place? This is the direction we want to go: a new “command history” tool that shows you the syntax to reproduce any command you have previously run. Would that meet your requirement?

If you really want to go the ImageJ1 arg string parsing route, a regex might be powerful enough to do it.

#5

Hi Curtis, thanks for the elaborated answer,

The key/value is not the problem indeed, it is rather passing from the macro-recorded string of parameters to this key/value format that was not straightforward, but I will definitly give a try to the ParseService.

Yes indeed, the idea was to have a mainScript for let say automated microscopy, that calls any external script or macro that can take an image as input, plus a set of arbitrary parameters and return a set of object-coordinates via the ResultTable.
I though that passing the path to the macro, and its argument directly via the GUI of the main script would be the most user-friendly as you can indeed copy/past from the macro recorder.

But in the end I think I will just pass a unique #@File from mainScript to subScript, and the parameters would be hardcoded in the subscript (eg a recorded macro) to avoid passing the String of arguments.
That’s more robust and probably less error prone.

Looking forward to see that :slight_smile:
I would not be able to say now if that would be the perfect solution for this particular case, but I like the idea.

1 Like