How to update properties of ROI simultaneously as its values in dialog box change

imagej1
roi-manager
javascript
imagej
roi

#1

I am a beginner and learning how to write a macro for ImageJ using Javascript.

I would like to create a macro to allow users to draw a line and update its properties. After user draws a line, the macro will add the drawn line to ROI manager and give a dialog box to update properties of the line which include the stroke width and color. Below is the example code I have written so far.

The problem I am having is that I can’t figure out how to get the properties (width/color) of the ROI (line) to update simultaneous as the user changes the value in the dialog box. Once the user clicks OK in the dialog, the macro should end. Notice in the code I am using a NonBlockingGenericDialog so that user can also re-position the line while also updating its properties simultaneously. One similar example of what I am trying to achieve is Analyze>>Tools>>Scale Bar plugin. Notice how in this plugin, as user changes the values in the dialog box, the scale bar in the image updates. For example, if a user changes the font size, the actual font size of scale bar on the image will simultaneously update.

Looking at ImageJ API, I noticed there are few methods that may help me (ex: addDialogListener and textValueChanged). However, I can’t figure out how to use these.

importClass(Packages.ij.IJ);
importClass(Packages.ij.WindowManager);
importClass(Packages.ij.plugin.frame.RoiManager);
importClass(Packages.ij.gui.NonBlockingGenericDialog);
importClass(Packages.ij.gui.RoiProperties);

img = WindowManager.getCurrentImage();
roi = img.getRoi(); // https://stackoverflow.com/a/35225648/4988010

handleROI(roi);

function handleROI(roi) {

    // IJ.log(roi)
    // IJ.log(roi == null)
    // IJ.log(roi.isLine())

    if (roi == null || !roi.isLine()) {
        // Exit macro if selection is not a line 
        IJ.log("Only line selection currenlty supported");
        return;
    }

    lineLength = roi.getLength(); // Get length of line

    manager = RoiManager.getInstance();
    if (manager == null)
        manager = new RoiManager();

    manager.addRoi(roi); // Add selection to ROI manager

    count = manager.getCount()
    roi = manager.getRoi(count - 1) // Set roi to the last added ROI 

    roi.setStrokeWidth(20); // Change width of roi to 20

    manager.select(count - 1) // Select it so its visible on image 

    gd = new NonBlockingGenericDialog("ROI Properties Update")
    gd.addNumericField("Width", 5, 0)
    gd.addStringField("Color", "red")
    //	gd.addDialogListener(this)
    gd.showDialog()

}

#2

I think this example could be interesting for you:

Gamma Adjuster Example:

https://imagej.nih.gov/ij/macros/js/Gamma_Adjuster.js


#3

@Bio7 Thanks for the example, it was helpful, but now I am having another issue.

In the code below, I create a “text roi” which reports the length a line selection drawn by the user. The user is given a dialog box to update the properties of the text such as font size, color, and location. The problem I am having is that the text properties are not updating on the image. For example, by default the text is red, however, after changing the color to blue, the text on the image still stays red. I am unsure why:

importClass(Packages.ij.IJ);
importClass(Packages.ij.WindowManager);
importClass(Packages.ij.plugin.frame.RoiManager);
importClass(Packages.ij.gui.NonBlockingGenericDialog);
importClass(Packages.ij.gui.RoiProperties);
importClass(Packages.ij.gui.DialogListener);
importClass(Packages.java.awt.Color);
importClass(Packages.java.awt.Font);
importClass(Packages.ij.gui.Toolbar);
importClass(Packages.ij.measure.Calibration);
importClass(Packages.ij.gui.TextRoi);


img = WindowManager.getCurrentImage();
roi = img.getRoi(); // https://stackoverflow.com/a/35225648/4988010


var text_font_size = 72;
var text_location = 'center';
var text_color = 'red';

handleROI(roi);

function handleROI(roi) {

    if (roi == null || !roi.isLine()) {
        // Exit macro if selection is not a line 
        print("Only line selection currenlty supported");
        return;
    }

    line_length = roi.getLength(); // Get length of line

    text_line_length = IJ.d2s(line_length, 1)


    manager = RoiManager.getInstance();
    if (manager == null)
        manager = new RoiManager();
    manager.reset(); // Erase all current ROI 

    manager.addRoi(roi); // Add selection to ROI manager


    count = manager.getCount()
    roi = manager.getRoi(count - 1) // Set roi to the last added ROI
    manager.select(count - 1) // Select it so its visible on image 

    text_pos = getTextLocationCoords(text_location);
    text_pos_x = text_pos.x;
    text_pos_y = text_pos.y;

    plain_font = new Font("Serif", Font.BOLD, 100);
    roi_text = new TextRoi(text_pos_x, text_pos_y, text_line_length, plain_font);
    roi_text.setStrokeColor(Color[text_color])
    manager.addRoi(roi_text);
    count = manager.getCount()
    manager.select(count - 1)

    manager.runCommand('Show All');
    img.updateAndRepaintWindow();

    listener = new DialogListener {
        dialogItemChanged: function(gd, event) {
            print('Inside: dialogItemChanged');

            text_font_size = gd.getNextNumber();
            text_color = gd.getNextString();
            text_location = gd.getNextChoice();

            updateROI();

            img.updateAndRepaintWindow(); // https://imagej.nih.gov/ij/developer/api/ij/ImagePlus.html#updateAndRepaintWindow--

            return true;
        }
    };

    gd = new NonBlockingGenericDialog("ROI Properties Update")
    gd.addNumericField("Font size", text_font_size, 0);
    gd.addStringField("Text color", text_color);
    gd.addChoice('Text location', ['top', 'center', 'bottom'], text_location);
    gd.addDialogListener(listener);
    gd.showDialog()

    function updateROI() {

        print(roi_text);

        plain_font = new Font("Serif", Font.BOLD, text_font_size);
        roi_text.setCurrentFont(plain_font);

        roi_text.setStrokeColor(Color[text_color]);

        text_pos = getTextLocationCoords(text_location);
        text_pos_x = text_pos.x;
        text_pos_y = text_pos.y;
        roi_text.setLocation(text_pos_x, text_pos_y);

        img.updateAndRepaintWindow(); // https://imagej.nih.gov/ij/developer/api/ij/ImagePlus.html#updateAndRepaintWindow--

    }

    function getTextLocationCoords(location) {
        pos_x = 0;
        pos_y = 0;

        if (location == 'center') {
            // Position the text at the center of the line 
            pos_x = (roi.x2 - roi.x1) / 2 + roi.x1;
            pos_y = (roi.y2 - roi.y1) / 2 + roi.y1;
        }

        if (location == 'top') {
            // Position the text at the start of the line 
            pos_x = roi.x1;
            pos_y = roi.y1;
        }

        if (location == 'bottom') {
            // Position the text at the start of the line 
            pos_x = roi.x2;
            pos_y = roi.y2;
        }

        return { x: pos_x, y: pos_y };
    }

}

#4

Please note that “ImageJ macro” is a particular script language described here. Writing in JavaScript is not a macro, but rather a script.

It seems like an issue with the ROI Manager. If you change the code to use an Overlay instead, then the text is refreshed when changed. Here is a version of your code that uses overlays:

importClass(Packages.ij.IJ);
importClass(Packages.ij.WindowManager);
importClass(Packages.ij.plugin.frame.RoiManager);
importClass(Packages.ij.gui.NonBlockingGenericDialog);
importClass(Packages.ij.gui.Overlay);
importClass(Packages.ij.gui.RoiProperties);
importClass(Packages.ij.gui.DialogListener);
importClass(Packages.java.awt.Color);
importClass(Packages.java.awt.Font);
importClass(Packages.ij.gui.Toolbar);
importClass(Packages.ij.measure.Calibration);
importClass(Packages.ij.gui.TextRoi);


img = WindowManager.getCurrentImage();
roi = img.getRoi(); // https://stackoverflow.com/a/35225648/4988010


var text_font_size = 72;
var text_location = 'center';
var text_color = 'red';

handleROI(roi);

function handleROI(roi) {

    if (roi == null || !roi.isLine()) {
        // Exit macro if selection is not a line 
        print("Only line selection currenlty supported");
        return;
    }

    line_length = roi.getLength(); // Get length of line

    text_line_length = IJ.d2s(line_length, 1)

	overlay = new Overlay(roi);
	img.setOverlay(overlay);

    text_pos = getTextLocationCoords(text_location);
    text_pos_x = text_pos.x;
    text_pos_y = text_pos.y;

    plain_font = new Font("Serif", Font.BOLD, 100);
    roi_text = new TextRoi(text_pos_x, text_pos_y, text_line_length, plain_font);
    roi_text.setStrokeColor(Color[text_color])
    overlay.add(roi_text);

    img.updateAndRepaintWindow();

    listener = new DialogListener {
        dialogItemChanged: function(gd, event) {
            print('Inside: dialogItemChanged');

            text_font_size = gd.getNextNumber();
            text_color = gd.getNextString();
            text_location = gd.getNextChoice();

            updateROI();

            img.updateAndRepaintWindow(); // https://imagej.nih.gov/ij/developer/api/ij/ImagePlus.html#updateAndRepaintWindow--

            return true;
        }
    };

    gd = new NonBlockingGenericDialog("ROI Properties Update")
    gd.addNumericField("Font size", text_font_size, 0);
    gd.addStringField("Text color", text_color);
    gd.addChoice('Text location', ['top', 'center', 'bottom'], text_location);
    gd.addDialogListener(listener);
    gd.showDialog()

    function updateROI() {

        print(roi_text);

        plain_font = new Font("Serif", Font.BOLD, text_font_size);
        roi_text.setCurrentFont(plain_font);

        roi_text.setStrokeColor(Color[text_color]);

        text_pos = getTextLocationCoords(text_location);
        text_pos_x = text_pos.x;
        text_pos_y = text_pos.y;
        roi_text.setLocation(text_pos_x, text_pos_y);

        img.updateAndRepaintWindow(); // https://imagej.nih.gov/ij/developer/api/ij/ImagePlus.html#updateAndRepaintWindow--

    }

    function getTextLocationCoords(location) {
        pos_x = 0;
        pos_y = 0;

        if (location == 'center') {
            // Position the text at the center of the line 
            pos_x = (roi.x2 - roi.x1) / 2 + roi.x1;
            pos_y = (roi.y2 - roi.y1) / 2 + roi.y1;
        }

        if (location == 'top') {
            // Position the text at the start of the line 
            pos_x = roi.x1;
            pos_y = roi.y1;
        }

        if (location == 'bottom') {
            // Position the text at the start of the line 
            pos_x = roi.x2;
            pos_y = roi.y2;
        }

        return { x: pos_x, y: pos_y };
    }

}

And here is a diff of the changes:

diff --git a/original.js b/overlay.js
index 25a23a4..0712ee8 100644
--- a/original.js
+++ b/overlay.js
@@ -1,7 +1,7 @@
 importClass(Packages.ij.IJ);
 importClass(Packages.ij.WindowManager);
-importClass(Packages.ij.plugin.frame.RoiManager);
 importClass(Packages.ij.gui.NonBlockingGenericDialog);
+importClass(Packages.ij.gui.Overlay);
 importClass(Packages.ij.gui.RoiProperties);
 importClass(Packages.ij.gui.DialogListener);
 importClass(Packages.java.awt.Color);
@@ -33,18 +33,8 @@ function handleROI(roi) {

     text_line_length = IJ.d2s(line_length, 1)

-
-    manager = RoiManager.getInstance();
-    if (manager == null)
-        manager = new RoiManager();
-    manager.reset(); // Erase all current ROI
-
-    manager.addRoi(roi); // Add selection to ROI manager
-
-
-    count = manager.getCount()
-    roi = manager.getRoi(count - 1) // Set roi to the last added ROI
-    manager.select(count - 1) // Select it so its visible on image
+       overlay = new Overlay(roi);
+       img.setOverlay(overlay);

     text_pos = getTextLocationCoords(text_location);
     text_pos_x = text_pos.x;
@@ -53,11 +43,8 @@ function handleROI(roi) {
     plain_font = new Font("Serif", Font.BOLD, 100);
     roi_text = new TextRoi(text_pos_x, text_pos_y, text_line_length, plain_font);
     roi_text.setStrokeColor(Color[text_color])
-    manager.addRoi(roi_text);
-    count = manager.getCount()
-    manager.select(count - 1)
+    overlay.add(roi_text);

-    manager.runCommand('Show All');
     img.updateAndRepaintWindow();

     listener = new DialogListener {

#5

@ctrueden Thank you for your help.

To all other readers learning how to write ImageJ scripts or macro, I have attached my final code and uploaded it to Github, Measure and Label Improved for ImageJ. I have built a script that will allow users to measure and label a line selection with its length. This script essentially does what was requested in this question: http://imagej.1557.x6.nabble.com/How-do-you-label-a-line-measurement-with-its-length-td5012782.html.