Login  Register

Re: plotwindow.drawPlot(plot) incessant resizing

Posted by Michael Schmid on Mar 11, 2020; 9:54am
URL: http://imagej.273.s1.nabble.com/plotwindow-drawPlot-plot-incessant-resizing-tp5022995p5023036.html

Hi Fred,

in the latest daily build (1.52u37), Wayne has put the ImageListener
callbacks into the EventQueue using EventQueue.invokeLater.
This should reduce the risk of deadlocks.

---
How to diagnose deadlocks:

Start ImageJ from the command line.

On Linux, type (in another terminal)
   kill -3 <pid>
where <pid> is the process ID of ImageJ as you get it, e.g., with
   ps -ef.

On windows, you may try to type ctrl-\ or ctrl-<break> in the terminal
where you have started ImageJ (I have no Windows computer here, so I
have not tried myself).

This gives you a thread dump, which tells you whether a deadlock has
been detected, which threads are involved and in which line of the
program they are stuck.

---
There is no way to remove ImageListeners if you don't know which class
instance has registered as ImageListener. I think that such a call would
be undesirable since it could lead to a bad programming habit of simply
deleting all ImageListeners, and then some other plugin still running
would be compromised.

So it is the responsibility of each plugin to de-register as
ImageListener; in case of doubt wrap everything into try-catch(-finally)
clauses where you deregister when something goes wrong. Also, if the
plugin has a "main" image that it works on and this one gets closed, the
ImageListener should detect this and deregister.


Michael
________________________________________________________________
On 08.03.20 20:51, Fred Damen wrote:

> 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
>

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