http://imagej.273.s1.nabble.com/plotwindow-drawPlot-plot-incessant-resizing-tp5022995p5023037.html
Thanks, this info will make debugging the next deadlock much easier.
wishing for stuff... I have not yet tried logImageListeners although I
registered for receiving the callbacks.
that when the instance goes away so does the registrations.
> 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>