Login  Register

Re: plotwindow.drawPlot(plot) incessant resizing

Posted by Fred Damen on Mar 08, 2020; 7:51pm
URL: http://imagej.273.s1.nabble.com/plotwindow-drawPlot-plot-incessant-resizing-tp5022995p5023034.html

Greetings Michael,

What I discovered the hard way was that if you create, and keep a list of,
many ImagePlus(s), with the ImageListener set, and then at a later time
try and delete the imageplus(s) on the list from within a ImageListener
callback, about 10-20% of the time ImageJ will lockup solid. This is
independent of removing the ______Listener(s), or not, before close()ing
the imageplus(s) within the ImageListener callback.  This never happened
with using only the WindowListener interface for this purpose as it seems
to only call the callback when a pre-identified imagewindow instance has
been effected.

lockups are a real joy to debug...

Is there a
ImagePlus.removeImageListener(n);
to go with the
ImagePlus.logImageListeners();
?

Enjoy,

Fred


On Thu, March 5, 2020 10:11 am, Michael Schmid wrote:

> Hi Fred,
>
> concerning the PlotMaker interface: Whenever the contents or the Roi of
> the source image is updated, the
>    public Plot getPlot()
> method of the PlotMaker is called.
> It automatically specifies the following 'useTemplate' flags (in
> addition to any other defined by the plot):
>    Plot.COPY_SIZE | Plot.COPY_LABELS | Plot.COPY_AXIS_STYLE |
>    Plot.COPY_CONTENTS_STYLE | Plot.COPY_LEGEND | Plot.COPY_EXTRA_OBJECTS
>
> The 'source image' is the one that the user plugin defines in
>    public ImagePlus getSourceImage()
>
> The plot.setPlotMaker(this) is not static, i.e., you have to call it
> with the first plot that you create and show. Currently, I think there
> is no simple way to start live plotting of a PlotMaker from java; the
> plot is 'live' only if the user presses the 'live' button. If live
> plotting from the start is desired, we would need a small modification
> of ImageJ.
>
>
> Concerning the RoiListener: The method
>    Roi.addRoiListener(this)
> is static, so a RoiListener's roiModified gets calls from all Rois,
> whatever image they belong to. The image is passed with
>    roiModified(ImagePlus img, int id)
> Based on the imp, you can select whether the event is of interest for
> your plugin or not. That's the same as for an ImageListener, where you
> should also which ImagePlus was affected (closed/opened/updated).
>
> Therfore, for both, the ImageListener and RoiListener interfaces, make
> sure to de-register with the corresponding
>    Roi.removeRoiListener(this);
>    ImagePlus.removeImageListener(this);
> Otherwise, your plugin will remain active in the background forever
> (until ImageJ closes) and receive the events, even if you thought it has
> ended all activity. In case of doubt, it does not hurt to call a
> remove...Listener too often (You can also call it without having
> registered with add...Listener).
>
> For the ImageListener interface, in the 1.52u daily build, the static
> method
>    ImagePlus.logImageListeners()
> can be used to check for plugins that have forgotten to de-register. You
> can easily call it from Javascript. There is no such method for
> RoiListeners (yet?).
>
>
> Best,
>
> Michael
> ________________________________________________________________
> On 03.03.20 05:26, Fred Damen wrote:
>> Greetings Michael,
>>
>> A mute point now, but, to reproduce, create hyperstack, create roi,
>> start
>> plugin, resize plotwindow, move the roi a dozen times.
>>
>> The plot.userTemplate works great; aggravation level plummets...
>>
>> For the PlotMaker example that you gave, what instigates the drawing of
>> the next plot. I assume that getPlot is called by PlotWindow, but how to
>> setup the trigger?
>>
>> For the RoiListener, who calls roiModified, i.e., static or instance?  I
>> found out the hard way that the ImageListener methods were called when
>> any
>> / all imageplus objects were affected; ugly race conditions when trying
>> to
>> close a list of images... use WindowListener instead...
>>
>> The attached plugin serves the same general purpose as the ProfilePlot,
>> albeit in the frame direction.  If you collected the same volume (stack
>> of
>> slices) repeatedly and you wanted to know if / how the signal changes
>> through the repeats.
>>
>> Thanks,
>>
>> Fred
>>
>> On Mon, March 2, 2020 4:32 am, Michael Schmid wrote:
>>> Hi Fred,
>>>
>>> there was no problem when I tried it, it works also after resizing the
>>> plot.
>>> Nevertheless, there is an elegant way to make the plot inherit the size
>>> of the previous one shown in the same PlotWindow:
>>>
>>>     plot.useTemplate(previousPlot, Plot.COPY_SIZE);
>>>
>>> You can also have it inherit other properties, such as the legend, axis
>>> labels, style, or curves added by the user (who had used the buttons at
>>> the bottom of the plot).
>>> Just use a bitwise OR or the sum of the following flags:
>>>
>>> COPY_SIZE    Flag for copying from a template: copy plot size
>>> COPY_LABELS  Flag for copying from a template: copy style & text of
>>> axis
>>> COPY_LEGEND  Flag for copying from a template: copy legend
>>> COPY_AXIS_STYLE  Flag for copying from a template: copy axis style
>>> COPY_CONTENTS_STYLE  Flag for copying from a template: copy contents
>>> COPY_EXTRA_OBJECTS Flag for copying PlotObjects (curves...) from a
>>> template if the template has more PlotObjects than the Plot to copy to.
>>>
>>>
>>> In your case (I did not really try to understand your plugin), it seems
>>> that the plot is supposed to react on changes of the Roi? Then there
>>> might be an easier option, just implement the PlotMaker Interface.
>>> The first 40 lines of the Profiler provide an example how to do this:
>>>
>>>     https://github.com/imagej/imagej1/blob/master/ij/plugin/Profiler.java
>>>
>>> If a PlotMaker is no option for you, there is also a RoiListener
>>> interface, which tells you when a Roi has changed, so you don't need
>>> the
>>> keyListener & MouseListener, and it will also detect changes of the Roi
>>> caused by, e.g., menu commands.
>>>
>>>     https://github.com/imagej/imagej1/blob/master/ij/gui/RoiListener.java
>>>
>>>
>>>
>>> [BTW, I did not understand your other post on CTRL-SHIFT under Fedora;
>>> is this about using the text tool on an image? Can you supply a
>>> screenshot to explain (Plugins>Utilities>Capture Delayed)? Replies in
>>> that thread, please]
>>>
>>> Michael
>>> ________________________________________________________________
>>> On 01.03.20 07:10, Fred Damen wrote:
>>>> Greetings,
>>>>
>>>> I have a plugin that plots the statistics of an ROI through the frame
>>>> dimension.  And replots this information when the Roi is altered.  The
>>>> trouble is that if you resize the plotwindow and then cause the plot
>>>> to
>>>> be
>>>> redrawn (plotwindow.drawPlot(plot);) the plotwindow appears to insist
>>>> upon
>>>> resizing the window somewhat asynchronously. Sometimes to the
>>>> interactively resized dimensions IRD, but mostly to default
>>>> dimensions.
>>>> So a naive attempt was to query the plotwindow size and set it again
>>>> after
>>>> the drawPlot. This results in the plotwindow more often being resized
>>>> to
>>>> the IRD, alas not always...  Figuring that there was one of those race
>>>> conditions going on I put in a IJ.wait statement, then the plotwindow
>>>> mostly resizes to the IRD, albeit the plotwindow does not get updated
>>>> during this wait, and also results in massive flickering.  Moving the
>>>> Roi
>>>> with the mouse shows the problem quicker than using the arrow keys,
>>>> but
>>>> within less than 10 redraws the problem happens.  Is there a way to
>>>> get
>>>> the plotwindow size not to change on a drawPlot?
>>>>
>>>> This has been happening for the past couple of years on most of the
>>>> systems I run this on.  Look for the IJ.wait(10); in the below plugin.
>>>>
>>>> Thanks in advance,
>>>>
>>>> Fred
>>>>
>>>> To reproduce:
>>>> imp = IJ.createImage("HyperStack", "32-bit grayscale-mode", 128, 128,
>>>> 1,
>>>> 1, 10);
>>>> imp.setRoi(new OvalRoi(42,52,42,33));
>>>>
>>>> Run this plugin, resize the plotwindow, then move the Roi:
>>>>
>>>> import ij.*;
>>>> import ij.plugin.*;
>>>> import ij.process.*;
>>>> import ij.gui.*;
>>>> import ij.util.Tools;
>>>> import java.io.*;
>>>> import java.awt.*;
>>>> import java.awt.event.*;
>>>> import java.util.*;
>>>> import ij.measure.*;
>>>> import java.awt.Rectangle;
>>>>
>>>>       /**
>>>>         This plugin continuously generates Frame-Dimension profile
>>>> plots
>>>> as
>>>> a selection
>>>>         is moved or resized through the XY Coordinate Plane.
>>>>
>>>>         @author Fred Damen <[hidden email]>
>>>>
>>>>         Version History:
>>>>         2018-04-01: Created
>>>>       */
>>>>
>>>>    public class F_Profiler implements PlugIn,
>>>>                                       MouseListener,
>>>>                                       MouseMotionListener,
>>>>                                       MouseWheelListener,
>>>>                                       Measurements,
>>>>                                       KeyListener,
>>>>                                       WindowListener,
>>>>                                       ImageListener {
>>>>       ImagePlus img;
>>>>       PlotWindow pwin;
>>>>       public double[] x;
>>>>       public double[] y;
>>>>       public double[] ye;
>>>>       String xLabel;
>>>>       String yLabel;
>>>>
>>>>       public void run(String arg) {
>>>>          img = IJ.getImage();
>>>>          int nf = img.getNFrames();
>>>>          if (nf<2) {
>>>>             IJ.showMessage("Dynamic F-Dimension Profiler", "This
>>>> command
>>>> requires a HyperStack.");
>>>>             return;
>>>>             }
>>>>
>>>>          img.getCanvas().addMouseListener(this);
>>>>          img.getCanvas().addMouseMotionListener(this);
>>>>          img.getCanvas().addKeyListener(this);
>>>>          img.getWindow().addMouseWheelListener(this);
>>>>          img.getWindow().addWindowListener(this);
>>>>          img.addImageListener(this);
>>>>          engaged = true;
>>>>          IJ.showStatus("F-Dimension Profile Engaged:
>>>> "+img.getTitle());
>>>>
>>>>          x  = new double[nf];
>>>>          y  = new double[nf];
>>>>          ye = new double[nf];
>>>>          String fu = img.getCalibration().getTimeUnit();
>>>>          try {
>>>>             ImageStack is = img.getStack();
>>>>             for(int f=0; f<nf; f++) {
>>>>                String[] strarr =
>>>> is.getSliceLabel(img.getStackIndex(1,1,f+1)).split(",|;",2)[0].split("
>>>> = |=| ",2);
>>>>                xLabel = strarr[0]+(fu!="" ? " ("+fu+")" : "");
>>>>                x[f] = Float.valueOf(strarr[1]).floatValue();
>>>>                //for(int ff=0; ff<f; ff++)
>>>>                //   if (x[f] == x[ff])
>>>>                //      throw new Throwable();
>>>>                //IJ.log("x["+f+"]="+x[f]);
>>>>                }
>>>>              }
>>>>          catch(Throwable e) {
>>>>              xLabel = "frame"+(fu!="" ? " ("+fu+")" : "");
>>>>              for(int f=0; f<nf; f++)
>>>>                 x[f] = f;
>>>>              }
>>>>
>>>>          yLabel = img.getTitle()+"
>>>> ("+img.getCalibration().getValueUnit()+")";
>>>>          if (!updateProfile())
>>>>             return;
>>>>          positionPlotWindow();
>>>>          }
>>>>
>>>>       boolean engaged = false;
>>>>       void disengage() {
>>>>          if (!engaged) return;
>>>>          if (img.getWindow() != null) {
>>>>             img.getCanvas().removeMouseListener(this);
>>>>             img.getCanvas().removeMouseMotionListener(this);
>>>>             img.getCanvas().removeKeyListener(this);
>>>>             img.getWindow().removeMouseWheelListener(this);
>>>>             img.getWindow().removeWindowListener(this);
>>>>             img.removeImageListener(this);
>>>>             }
>>>>          pwin = null;
>>>>          engaged = false;
>>>>          IJ.showStatus("F-Dimension Profile Disengaged");
>>>>          }
>>>>
>>>>
>>>>       boolean updateProfile() {
>>>>          Roi roi = img.getRoi();
>>>>          if (img == null || roi == null) {
>>>>             IJ.showStatus("Frame-Dimension Profiles running but
>>>> nothing
>>>> to
>>>> do");
>>>>             return true;
>>>>             }
>>>>
>>>>          if ((pwin != null) && (!pwin.isVisible())) {
>>>>             IJ.log("F_Profiler: should not have reached here
>>>> '"+pwin+"'");
>>>>             engaged = true;
>>>>             disengage();
>>>>             return false;
>>>>             }
>>>>
>>>>          int nf = img.getNFrames();
>>>>          int cs = img.getZ();
>>>>          double[] yM = new double[nf];
>>>>          double[] ym = new double[nf];
>>>>          double[] ys = new double[nf];
>>>>          ImageStack is = img.getStack();
>>>>          Calibration cal = img.getCalibration();
>>>>          for(int f=0; f<nf; f++) {
>>>>             ImageProcessor ip =
>>>> is.getProcessor(img.getStackIndex(1,cs,f+1));
>>>>             ip.setRoi(roi);
>>>>             ImageStatistics stats = ImageStatistics.getStatistics(ip,
>>>> MEAN+STD_DEV+MIN_MAX+MEDIAN, cal);
>>>>             y[f]  = stats.mean;
>>>>             ye[f] = stats.stdDev;
>>>>             yM[f] = stats.max;
>>>>             ym[f] = stats.min;
>>>>             ys[f] = stats.median;
>>>>             }
>>>>
>>>> Plot plot = new Plot("Frame Profile ("+img.getTitle()+")", xLabel,
>>>> yLabel);
>>>>           plot.setFont(new Font("Comic Sans MS", Font.PLAIN, 20));
>>>>           plot.setXLabelFont(new Font("Comic Sans MS", Font.PLAIN,
>>>> 24));
>>>>           plot.setYLabelFont(new Font("Comic Sans MS", Font.PLAIN,
>>>> 24));
>>>>
>>>>           plot.setColor(Color.blue);
>>>>           plot.setLineWidth(1);
>>>>           plot.addPoints(x,y,ye,Plot.X);
>>>>           plot.setColor(Color.red);
>>>>           plot.setLineWidth(4);
>>>>           plot.addPoints(x,y,Plot.X);
>>>>
>>>>           plot.setColor(Color.green);
>>>>           plot.setLineWidth(2);
>>>>           plot.addPoints(x,ym,Plot.BOX);
>>>>           plot.addPoints(x,yM,Plot.BOX);
>>>>           plot.setColor(Color.black);
>>>>           plot.addPoints(x,ys,Plot.CIRCLE);
>>>>           plot.setLegend("stddev\nmean\nmax\nmin\nmedian",Plot.AUTO_POSITION);
>>>> if (pwin==null) {
>>>>              pwin = plot.show();
>>>>              pwin.addWindowListener(this);
>>>>              }
>>>>           else {
>>>>              Dimension s = pwin.getSize();
>>>>              pwin.drawPlot(plot);
>>>>              pwin.setSize(s);
>>>> IJ.wait(10);
>>>>              pwin.setSize(s);
>>>>              }
>>>>           plot.setLimitsToFit(true);
>>>>
>>>>           return true;
>>>>           }
>>>>
>>>>      void positionPlotWindow() {
>>>>          IJ.wait(500);
>>>>          if (pwin==null || img==null) return;
>>>>          ImageWindow iwin = img.getWindow();
>>>>          if (iwin==null) return;
>>>>          Dimension screen =
>>>> Toolkit.getDefaultToolkit().getScreenSize();
>>>>          Dimension plotSize = pwin.getSize();
>>>>          Dimension imageSize = iwin.getSize();
>>>>          if (plotSize.width==0 || imageSize.width==0) return;
>>>>          Point imageLoc = iwin.getLocation();
>>>>          int w = imageLoc.x+imageSize.width+10;
>>>>          if (w+plotSize.width>screen.width)
>>>>             w = screen.width-plotSize.width;
>>>>          pwin.setLocation(w, imageLoc.y);
>>>>          iwin.toFront();
>>>>          }
>>>>
>>>>       public void mousePressed(MouseEvent e) {
>>>>          Roi roi = img.getRoi();
>>>>          int ix,iy;
>>>>          if (roi == null) {
>>>>             Point here = img.getCanvas().getCursorLoc();
>>>>             ix = here.x;
>>>>             iy = here.y;
>>>>             }
>>>>          else if (roi.getType() == Roi.POINT) {
>>>>             Rectangle bounds = roi.getBounds();
>>>>             ix = bounds.x;
>>>>             iy = bounds.y;
>>>>             }
>>>>          else {
>>>>             updateProfile();
>>>>             return;
>>>>             }
>>>>
>>>>          int nf = img.getNFrames();
>>>>          int cs = img.getZ();
>>>>          ImageStack is = img.getStack();
>>>>          for(int f=0; f<nf; f++) {
>>>>             ImageProcessor ip =
>>>> is.getProcessor(img.getStackIndex(1,cs,f+1));
>>>>             y[f]  = ip.getPixelValue(ix, iy);
>>>>             ye[f] = 0;
>>>>             }
>>>>
>>>> Plot plot = new Plot("Frame Profile ("+img.getTitle()+")", xLabel,
>>>> yLabel);
>>>>           plot.setFont(new Font("Comic Sans MS", Font.PLAIN, 20));
>>>>           plot.setXLabelFont(new Font("Comic Sans MS", Font.PLAIN,
>>>> 24));
>>>>           plot.setYLabelFont(new Font("Comic Sans MS", Font.PLAIN,
>>>> 24));
>>>>
>>>>           plot.setColor(Color.blue);
>>>>           plot.setLineWidth(4);
>>>>
>>>>           Calibration cal = img.getCalibration();
>>>>           plot.setJustification(Plot.RIGHT);
>>>>           plot.addLabel(0.99,0.99,String.format("%.2f(%d), %.2f(%d),
>>>> (%d)",
>>>>                         cal.getX(ix),ix,cal.getY(iy),iy,img.getT()));
>>>>
>>>>           plot.addPoints(x,y,ye,Plot.X);
>>>> if (pwin==null) {
>>>>              pwin = plot.show();
>>>>              pwin.addWindowListener(this);
>>>>              }
>>>>           else {
>>>>              Dimension s = pwin.getSize();
>>>>              pwin.drawPlot(plot);
>>>>              pwin.setSize(s);
>>>>              }
>>>>           plot.setLimitsToFit(true);;
>>>>           }
>>>>
>>>>       public void mouseDragged(MouseEvent e)  { updateProfile(); }
>>>>       public void keyReleased(KeyEvent e)     { updateProfile(); }
>>>>
>>>>       public void keyPressed(KeyEvent e)      {}
>>>>       public void keyTyped(KeyEvent e)        {}
>>>>       public void mouseReleased(MouseEvent e) {}
>>>>       public void mouseExited(MouseEvent e)   {}
>>>>       public void mouseClicked(MouseEvent e)  {}
>>>>       public void mouseEntered(MouseEvent e)  {}
>>>>       public void mouseMoved(MouseEvent e)    {}
>>>>
>>>>       public void mouseWheelMoved(MouseWheelEvent e) { /*
>>>> updateProfile(); */ }
>>>>
>>>>       public void windowActivated(WindowEvent e)   {}
>>>>       public void windowClosed(WindowEvent e)      { disengage();}
>>>>       public void windowClosing(WindowEvent e)     { disengage();}
>>>>       public void windowDeactivated(WindowEvent e) {}
>>>>       public void windowDeiconified(WindowEvent e) {}
>>>>       public void windowIconified(WindowEvent e)   {}
>>>>       public void windowOpened(WindowEvent e)      {}
>>>>
>>>>       public void imageClosed(ImagePlus imp) {}
>>>>       public void imageOpened(ImagePlus imp) {}
>>>>       public void imageUpdated(ImagePlus imp) { /* if (imp==img)
>>>> updateProfile(); */}
>>>> }
>>>>
>>>> --
>>>> 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
>>
>
> --
> ImageJ mailing list: http://imagej.nih.gov/ij/list.html
>

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html