Using runMacro(...) on a macro with Script Parameters

imagej
macro
scriptparameters

#1

Hi all,

If we use the wonderful scripting parameters, as far as I understand we can no longer use “Install Macros” or “Run Macro…”.

I know that IJM is a bit deprecated and we’re moving towards scripting languages, but I was just curious if it was possible to do the following.

  1. Get the full path to a macro, which contains SciJava Scripting Parameters
  2. Programatically execute this macro (ideally from another macro)

Right now it just executes it, ignoring the script parameters, just like Run"Macro".

Any thoughts are more than welcome.

Oli


#2

I suppose you’re talking about the menu commands Macros > Install Macros and Macros > Run Macro available from an ImageJ 1.x Text Window? If so, you’re probably right, because this text window is created by the ij.plugin.frame.Editor class and is deep in ImageJ1 land (i.e. it has its own logic for script languages and has hard-coded calls to ij.plugin.macro.Interpreter).

If you use the (ImageJ2) script editor, you have the Run button as well as the Run > Run menu command that allow to run the current macro.

The Install functionality (i.e. Macros > Install Macros from the text window, or Plugins > Macros > Install… in the main menu) is indeed something that currently doesn’t support script parameters. Those parameters are parsed only once before the script is being run. Using the macro {} syntax with parameters would require parameter harvesting to be performed each time a macro snippet is run. (@ctrueden and @stelfrich might want to comment on this.) However, if you split each macro into a separate .ijm file, you can achieve the same functionality, see my suggestions below.

It’s not deprecated: ImageJ2 supports the IJ1 Macro language as a first-class citizen among all the supported scripting languages. I would rather say the macro language has its limitations by supporting only text and numerical variable types, and no objects that would allow to use the Java API (e.g. File objects, or services such as OpService).


What do you mean? From the menu path, getting the path on the file system? You don’t need this, as you can call scripts both from files or via their menu path.

From script languages that support Java objects (i.e. all but IJ1 macro), you can use SciJava services to run other scripts. For example, given a macro My_Macro.ijm:

#@ String text
print (text)

you can call this from other scripts as follows:

  • for script files anywhere on your file system:

    #@ ScriptService scripts
    // show a new dialog to harvest script parameters
    scripts.run(new File("/path/to/My_Macro.ijm"), true)
    // or use the given parameter(s) in key, value pairs
    scripts.run(new File("/path/to/My_Macro.ijm"), true, "text", "given text parameter")
    
  • for scripts anywhere in your menu (where the path might be unknown):

    #@ ModuleService modules
    modules.run(modules.getModuleById("script:Plugins/My Menu/My_Macro.ijm"), true)
    

(See the javadoc for ScriptService and ModuleService.)

That means that if you save your macro in e.g. ./Fiji.app/scripts/Plugins/My_Menu, it will be callable just like any other command. From other macros (where ScriptService and ModuleService are not available), you can then still run the macro recorder to get the required command, such as:

run("My Macro", "text=[given text parameter]");

Hope that helps.


#3

While ImageJ2’s ScriptLanguage plugin for the IJ1 macro language enables execution of macros as SciJava modules, there is no logic to support the macro {} syntax. Without digging deeply in the code, my intuition is that supporting that would require some more ImageJ Legacy surgery.

I am reluctant to invest time supporting things like “Install Macros” and “Run Macro” because you can mostly achieve them in other ways. And to be honest, I never use them. For those who find them really useful, it would be good to have a discussion about why, and then we can figure out the best way to improve the situation. My guess is that the most useful missing thing is the keyboard shortcut customization, which I bet we could add to ImageJ2 in a more general way—that is something I’ve wanted for a long time but had no time to work on.


#4

I vaguely remember a discussion about defining the menuPath in #@ annotation. Maybe we can combine that with the option that drag&dropping an IJM on the toolbar “installs” it to the ImageJ/Fiji scripts folder s.t. it is located at Plugins > Macros > filename. That way, we could at least emulate the behavior of “Install Macros” (without supporting multiple macros in a file). It would, however, mean that drag&drop behavior changes.

Also, @ctrueden, is there a reason why we don’t have a Script Editor menu entry? (sorry, a little of topic)


#5

Yeah, we were discussing about this on gitter (though not exactly menuPath, but Commands from scripts in general), and subsequently in Dresden, part of this is summarized in this issue:

https://github.com/scijava/scijava-common/issues/261#issuecomment-310598849

The original focus was on other script languages, but if we create a #@script annotation:

#@script (name = "my-utils", menuPath = "Plugins>My Cool Script")

this would work for IJ1 macros as well, of course.


#6

Hi!

I was talking about both the menu entries and the IJ1 Macro command runMacro(path, args).

To describe why this became a question, we were trying to run a macro from another macro, and it so happened that this second macro had some script parameters that did not get recognized with the runMacro() command.

Thank you for that bit of information, but here we were talking about a macro that does not have a menu entry and resides in a user’s folder somewhere on a server.

Great! That is exactly what I needed to know!

I know this is rather hacky, but for the proof of concept of what I was going for (call another macro inside another IJ1 Macro, I chose this alternative.

  1. I made a simple ImageJ2 Command that has a @File parameter, which then runs scripts.run()
  2. This command is macro record-able and effectively works with the script parameters from within an ImageJ1 Macro

Here is the tiny Command that does this

package ch.epfl.biop.macrorunner;

import java.io.File;
import java.io.FileNotFoundException;

import javax.script.ScriptException;

import org.scijava.command.Command;
import org.scijava.log.LogService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.script.ScriptService;

@Plugin(type= Command.class, menuPath="Plugins>BIOP>BIOP Run Macro...")
public class B_Run_Macro implements Command {
	
	@Parameter
	private File macro_file;
	
	@Parameter
	private LogService logService;
	
	@Parameter
	private ScriptService scriptService;
	
	@Override
	public void run() {
		try {
			scriptService.run(macro_file, true);
		} catch (FileNotFoundException | ScriptException e) {
			// TODO Auto-generated catch block
			logService.error(e.toString());
		}
	}

}

Effectively for us, it replaces the runMacro() command, without having to modify anything else in ImageJ/Fiji.

Knowing that this is a rather niche need, we are satisfied with this hacky solution, though comments and throughs are always welcome!

And thank you all for your time and support!


Scripting parameters in Action Bar plugin
#7

@oburri Cute hack! I was wondering though why you need a Command rather than e.g. a script like:

#@File macro_file
#@ScriptService scriptService
scriptService.run(macro_file, true)

And then save it as scripts/Plugins/BIOP/BIOP_Run_Macro.groovy.

Was there some obstacle to that working?


#8

@ctrueden :blush:

There was no particular need, it was more of a way to make a usefully minimalest-est imageJ2 Command, so that I could work it in Eclipse from scratch in 30 minutes. In practice, it really helped me get a better feel for SciJava.


#9

Is there an alternative to the Script services to run a macro file with parameters ?
I would like to run it from a compiled jython script, so I can’t use services.

I tried to use the MacroRunner class, but apparently I cant use the script parameters.
I just need to tell the macro to run using the image in memory.

#@ File (Label='Macro file') MacroFile
from ij       import IJ
from ij.macro import MacroRunner

ImagePath = ...
Image     = IJ.openImage(ImagePath)
#Image.show() # this works for the macro then, but I dont want to display the image

macro = MacroRunner(MacroFile)
macro.run() # is there a way to precise to use the image above ?

Example of macro that is called :
run("Find Edges");


#10

MacroRunner is an IJ1 class, so you’ll have no access to any SciJava functionality (such as script parameters) from within it.


You can still use SciJava services. If getting them via script parameters is not possible in your case (how are you running your compiled script, by the way?), you can still ask the current SciJava Context for its services:

from org.scijava.script import ScriptService
scriptService = context.getService(ScriptService)

The question is rather how to retrieve the Context. I found the following ways in Jython (maybe others can chime in with better advice here):

  • using Python locals:

    context = locals()['org.scijava.script.ScriptModule'].getContext()
    
  • using IJ1Helper from imagej-legacy:

    from net.imagej.legacy import IJ1Helper
    context = IJ1Helper.getLegacyContext()
    

Independent of this, if you have your macro code in a file (as opposed to handing over a string to a method), you can also put your macro into the ./Fiji.app/scripts/ folder and use the code that gets recorded by the recorder in your Python code, e.g.:

IJ.run(imp, "My Macro", "some_option")

Setting persistence for parameters of GenericDialog in Jython
#11

Great thanks for the complete answer !

I am importing some function from this subscript into a “main” script, therefore it gets compiled.
I have a set of such subscripts depending on the choice of the user.
One would be to let them provide a home-made/recorded macro for some pre-processing.

That’s a nice simple solution, I could tell the user to put into the script folder but I guess an even simpler way would be to create a copy of the macro file provided by the user into this directory.
This way I dont have to fiddle with the name of the macro neither !


#12

In this case, you can provide the required parameters/services as a function parameter, no?
Something along these lines:

#@ ScriptService scriptService
import my_module # containing my_function

my_function(scriptService, some_other_parameter)

Note though that in order to make the macro available in the menu (and therefore accessible by the IJ.run command), you might have to restart your ImageJ instance.


#13
  • This works :
  • This works too :
  • This last one did not work in a script that is imported

-> KeyError: 'org.scijava.script.ScriptModule'