Greetings,
Just in case someone else finds the need for copying the exact zoom and/or
cursor location...
Copy Exact Zoom: Taken predominately from Zoom.setZoom: if
areaselection... with fix(?)
ImageCanvas fic = fimp.getCanvas();
ImageCanvas tic = timp.getCanvas();
Insets insets = fimp.getWindow().getInsets();
Rectangle srcRect = fic.getSrcRect();
double mag = fic.getMagnification();
int cw = (int)(srcRect.width*mag + insets.right+insets.left +
ImageWindow.HGAP*2);
int ch = (int)(srcRect.height*mag + insets.top+insets.bottom +
ImageWindow.VGAP*2+fimp.getWindow().getSliderHeight());
tic.setSourceRect(srcRect);
tic.setMagnification(mag);
tic.setSize(new Dimension((int)(srcRect.width*mag),
(int)(srcRect.height*mag)));
timp.getWindow().setSize(cw,ch);
// lacking from the stolen code snippet...
// for the zoom to occur you need to...
tic.repaint();
Copy the cursor:
Code snippet taken from SyncWindows.drawSyncCursor. Trouble with using it
directly is that it is private and not static.
1) make a Shape (specifically GeneralPath) where the locations are zero
(origin) based.
public static Shape makeShapeX(int near, int far) {
GeneralPath path = new GeneralPath();
path.moveTo(-far, -far); path.lineTo(-near, -near);
path.moveTo(+far, +far); path.lineTo(+near, +near);
path.moveTo(-far, +far); path.lineTo(-near, +near);
path.moveTo(+far, -far); path.lineTo(+near, -near);
return new GeneralPath(path);
}
2) Convert to an Roi at the cursor location in image coordinates...
public static Roi makeCursorRoi(ImageCanvas ic, Point p, Shape shape) {
Rectangle sr = ic.getSrcRect();
double mag = ic.getMagnification();
AffineTransform at = new AffineTransform(1.0/mag,0.0, 0.0,1.0/mag,
sr.x+p.x,sr.y+p.y);
return new ShapeRoi(at.createTransformedShape(shape));
}
3) set this ROI as a cursor in the desired window as an overlay...
public static boolean setCursor(ImagePlus imp, Roi cursor) {
cursor.setStrokeColor(Color.red);
cursor.setStrokeWidth(2);
cursor.setNonScalable(true);
cursor.setIsCursor(true);
Overlay o = removeCursor(imp);
if (o == null) return false;
o.add(cursor);
imp.setOverlay(o);
return true;
}
Bonus gotchas:
timp.settimp.setDisplayRange(fimp.getDisplayRangeMin(),fimp.getDisplayRangeMax());
// needs following for the display change
timp.updateAndDraw();
>From memory (and docs)... imp.repaintWindow does not update the slice
label if had been changed.
Fred
PS: non-minimal demonstrative code attached.
On Mon, December 8, 2025 11:42 am, Fred Damen wrote:
> Greetings, Kenneth and others,
>
> Thanks for the quick response, although, let me redirect the intent of
> what I am looking for in a response... Some times doing something in Java
> in ImageJ just works if you get the steps just right; lifting code
> snippets from publicly available code is a great way of accomplishing this
> - although this requires finding this code. When writing the code simply
> from reading the documentation sometimes only kinda-sorta works, and you
> spend way to much time trying to get it to work.
>
> For example, some times opening a series of ImagePlus(s) (which includes
> zooming) one of the ImageWindows which exists and is displayed, albeit the
> ImageWindow is a couple of pixels width and the canvas size is something
> like -1,35 (from memory 35 is the height of the title bar). In this
> condition, using ic.zoom100Percent() alone does not resize the window. But
> if, when detected, you set the canvas size to something legit,
> ic.setSize(imp.getWidth(),imp.getHeight()), the ic.zoom100Percent() works
> as expected, n.b., any legit size works. During the debugging, I noticed
> that if I can make said ImageWindow active and use the GUI Image>Zoom>View
> 100% it seems to always zooms to 100%; Why this GUI works and not what is
> supposedly called does not???
>
> So, if someone has code snippets that can replicate the specific geometry
> and zoom conditions, and/ot place something visible (but does not modify /
> function) at a point in said other window, and is willing to share, my
> happiness level will rise significantly.
>
> When I asked for code snippet for scrolling large tables within a Panel,
> something I struggled with, Curtis provided a code snippet that worked
> perfectly, Thanks again Curtis...
>
> Thanks in advance,
>
> Fred
>
> On Mon, December 8, 2025 3:23 am, Kenneth Sloan wrote:
>> The way I do this is:
>>
>> a) define a Class that extends ImageWindow
>> b) define methods such as âSetZoom(â¦)â, and âSetCursorâ
>> c) define methods such as âLinkToWindow(MyImageWindow)
>> d) In the code that creates each such window, use âLinkToWindowâ to
>> tell all the windows about each other
>> e) in the MyImageWindow class, catch all of the changes you want to
>> handle, and notify all of the windows you are linked to. BEWARE OF
>> LOOPS.
>>
>> If there is a better way to do this kind of thing, Iâm all ears - but
>> this has worked for me.
>>
>> Note: I do this for a slightly different reason, but the general idea is
>> to:
>>
>> a) create an extension class
>> b) link the windows
>> c) modify the extension class to accept the links, capture the changes,
>> and communicate them.
>>
>> --
>> Kenneth Sloan
>> [email protected]
>> Vision is the art of seeing what is invisible to others.
>>
>>
>> --
>> ImageJ mailing list: http://imagej.nih.gov/ij/list.html
>>
>
> --
> ImageJ mailing list: http://imagej.nih.gov/ij/list.html
>
--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html
import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import ij.plugin.*;
import ij.plugin.frame.*;
public class Copy_N_Paste implements PlugIn {
public void run(String arg) {
ImagePlus imp = IJ.getImage();
switch(arg.toLowerCase()) {
case "": break;
case "from": copyFrom(imp); return;
case "zoom": pasteZoom(imp); return;
case "czt": pasteCZT(imp); return;
case "displayrange": pasteDisplayRange(imp); return;
case "addmenu": addMenu(); return;
case "removemenu": removeMenu(); return;
default: IJ.showStatus("Copy_N_Paste: illegal arg '"+arg+"'");
return;
}
GenericDialog gd = new GenericDialog("Copy_N_Paste");
gd.addChoice("Which:", new String[]{"Zoom", "CZT", "Display Range",
"Cursor", "Add Menu", "Remove Menu"}, "");
gd.addImageChoice("From", imp.getTitle());
gd.addImageChoice("To:", "");
gd.showDialog();
if (gd.wasCanceled()) return;
String which = gd.getNextChoice();
ImagePlus fimp = gd.getNextImage();
ImagePlus timp = gd.getNextImage();
switch(which) {
case "Zoom": copyZoom(fimp, timp); return;
case "CZT": copyCZT(fimp, timp); return;
case "Display Range": copyDisplayRange(fimp, timp); return;
//case "Cursor": copyCursor(fimp, timp); return;
case "Add Menu": addMenu(); return;
case "Remove Menu": removeMenu(); return;
default: IJ.showStatus("Copy_N_Paste: illigal option '"+which+"'");
}
}
public static void copyFrom(ImagePlus fimp) {
IJ.setProperty("Copy_N_Paste:From",fimp);
}
public static boolean pasteZoom(ImagePlus timp) {
ImagePlus fimp = (ImagePlus)IJ.getProperty("Copy_N_Paste:From");
if (fimp == null) {
IJ.error("Copy_N_Paste: From ImagePlus not set.");
return false;
}
return copyZoom(fimp,timp);
}
public static boolean copyZoom(ImagePlus fimp, ImagePlus timp) {
if (fimp == null) return false;
if (timp == null) return false;
//fimp.waitTillActivated(fimp);
//timp.waitTillActivated(fimp);
ImageCanvas fic = fimp.getCanvas();
ImageCanvas tic = timp.getCanvas();
Insets insets = fimp.getWindow().getInsets();
Rectangle srcRect = fic.getSrcRect();
double mag = fic.getMagnification();
int cw = (int)(srcRect.width*mag + insets.right+insets.left +
ImageWindow.HGAP*2);
int ch = (int)(srcRect.height*mag + insets.top+insets.bottom +
ImageWindow.VGAP*2+fimp.getWindow().getSliderHeight());
tic.setSourceRect(srcRect);
tic.setMagnification(mag);
tic.setSize(new Dimension((int)(srcRect.width*mag),
(int)(srcRect.height*mag)));
timp.getWindow().setSize(cw,ch);
//timp.getWindow().pack();
tic.repaint();
//IJ.wait(100);
return true;
}
public static boolean pasteCZT(ImagePlus timp) {
ImagePlus fimp = (ImagePlus)IJ.getProperty("Copy_N_Paste:From");
if (fimp == null) {
IJ.error("Copy_N_Paste: From ImagePlus not set.");
return false;
}
return copyCZT(fimp,timp);
}
public static boolean copyCZT(ImagePlus fimp, ImagePlus timp) {
if (fimp == null) return false;
if (timp == null) return false;
timp.setPosition(fimp.getNChannels()== 1 ? 1 : fimp.getC(),
fimp.getNSlices() == 1 ? 1 : fimp.getZ(),
fimp.getNFrames() == 1 ? 1 : fimp.getT());
return true;
}
public static boolean pasteDisplayRange(ImagePlus timp) {
ImagePlus fimp = (ImagePlus)IJ.getProperty("Copy_N_Paste:From");
if (fimp == null) {
IJ.error("Copy_N_Paste: From ImagePlus not set.");
return false;
}
return copyDisplayRange(fimp,timp);
}
public static boolean copyDisplayRange(ImagePlus fimp, ImagePlus timp) {
if (fimp == null) return false;
if (timp == null) return false;
timp.setDisplayRange(fimp.getDisplayRangeMin(),fimp.getDisplayRangeMax());
//timp.getCanvas().draw();
//timp.repaintWindow();
timp.updateAndDraw();
//timp.updateAndRepaintWindow();
return true;
}
public static Shape makeShape() {
return makeShapeX();
}
public static int defaultNear = 2;
public static int defaultFar = 16;
public static Shape makeShapeX() {
return makeShapeX(defaultNear, defaultFar);
}
public static Shape makeShapeX(int near, int far) {
GeneralPath path = new GeneralPath();
path.moveTo(-far, -far); path.lineTo(-near, -near);
path.moveTo(+far, +far); path.lineTo(+near, +near);
path.moveTo(-far, +far); path.lineTo(-near, +near);
path.moveTo(+far, -far); path.lineTo(+near, -near);
return new GeneralPath(path);
}
public static Roi makeCursorRoi(ImageCanvas ic, int x, int y, Shape shape) {
return makeCursorRoi(ic, new Point(x,y), shape);
}
public static Roi makeCursorRoi(ImageCanvas ic, Shape shape) {
return makeCursorRoi(ic, ic.getCursorLoc(), shape);
}
public static Roi makeCursorRoi(ImageCanvas ic, Point p, Shape shape) {
Rectangle sr = ic.getSrcRect();
double mag = ic.getMagnification();
AffineTransform at = new AffineTransform(1.0/mag,0.0, 0.0,1.0/mag,
sr.x+p.x,sr.y+p.y);
return new ShapeRoi(at.createTransformedShape(shape));
}
public static boolean pasteCursor(ImagePlus timp) {
ImageCanvas ic = timp.getCanvas();
Point p = ic.getCursorLoc();
Roi cursor = makeCursorRoi(ic, p, makeShapeX(defaultNear, defaultFar));
Overlay o = removeCursor(timp);
return setCursor(timp, cursor);
}
public static boolean copyCursor(Shape shape, ImagePlus fimp, ImagePlus ...
timp) {
if (shape == null) return false;
if (fimp == null) return false;
Roi cursor = makeCursorRoi(fimp.getCanvas(), shape);
for(int i=0; i<timp.length; i++)
if (timp[i] != null)
setCursor(timp[i], cursor);
return true;
}
public static boolean copyCursor(ImagePlus fimp, ImagePlus timp) {
return copyCursor(fimp, timp, makeShapeX(defaultNear, defaultFar));
}
public static boolean copyCursor(ImagePlus fimp, ImagePlus timp, int near,
int far) {
return copyCursor(fimp, timp, makeShapeX(near, far));
}
public static boolean copyCursor(ImagePlus fimp, ImagePlus timp, Shape
shape) {
if (fimp == null) return false;
if (timp == null) return false;
ImageCanvas ic = fimp.getCanvas();
if (ic==null || !ic.cursorOverImage()) return false;
return setCursor(timp, makeCursorRoi(ic, shape));
}
public static Overlay removeCursor(ImagePlus imp) {
if (imp == null) return null;
Overlay o = imp.getOverlay();
if (o == null)
return new Overlay();
for(int i=o.size()-1; i>=0; i--)
if (o.get(i)!=null && o.get(i).isCursor())
o.remove(i);
imp.setOverlay(o);
return o;
}
public static boolean setCursor(ImagePlus imp, Roi cursor) {
cursor.setStrokeColor(Color.red);
cursor.setStrokeWidth(2);
cursor.setNonScalable(true);
cursor.setIsCursor(true);
Overlay o = removeCursor(imp);
if (o == null) return false;
o.add(cursor);
imp.setOverlay(o);
return true;
}
private static String menuName = "Copy_N_Paste";
public static void addMenu() {
removeMenu();
PopupMenu ppm = Menus.getPopupMenu();
Menu pm = new Menu(menuName);
ppm.add(pm);
MenuItem dmi = new MenuItem("Dialog");
dmi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Copy_N_Paste().run("");
}
});
pm.add(dmi);
MenuItem frommi = new MenuItem("Mark From");
frommi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ImagePlus imp = WindowManager.getCurrentImage();
copyFrom(imp);
}
});
pm.add(frommi);
pm.addSeparator();
MenuItem tozmi = new MenuItem("Zoom");
tozmi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ImagePlus imp = WindowManager.getCurrentImage();
pasteZoom(imp);
}
});
pm.add(tozmi);
MenuItem todrmi = new MenuItem("Display Range");
todrmi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ImagePlus imp = WindowManager.getCurrentImage();
pasteDisplayRange(imp);
}
});
pm.add(todrmi);
MenuItem tocztmi = new MenuItem("CZT");
tocztmi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ImagePlus imp = WindowManager.getCurrentImage();
pasteCZT(imp);
}
});
pm.add(tocztmi);
MenuItem tczmi = new MenuItem("TestCursor");
tczmi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ImagePlus imp = WindowManager.getCurrentImage();
pasteCursor(imp);
}
});
pm.add(tczmi);
}
private static boolean removeMenu() {
PopupMenu ppm = Menus.getPopupMenu();
int n = ppm.getItemCount();
for(int i=n-1; i>=0; i--)
if (ppm.getItem(i).getLabel().equals(menuName)) {
((Menu)ppm.getItem(i)).removeAll();
ppm.remove(i);
return true;
}
return false;
}
}
--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html