ImageViewer .show() opens undesirable extra figure canvas

Hello! I’m attempting to make a simple interactive pixel calibration window for a pyqt5 project, and I discovered skimage.viewer.canvastools.LineTool. It works quite well, except for a behavior of the ImageViewer that pops an extra empty matplotlib figure canvas. I’m digging all over in the ImageViewer object attributes to look for something to talk to this extra figure window, but no luck! My goal is to be able to programatically close the viewer in order to proceed in my routine, but the console is held up until I manually close the extra figure window!

Here’s a code snippet:

from skimage import data
from skimage.viewer.canvastools import LineTool
from skimage.viewer import ImageViewer

im = data.camera()

viewer = ImageViewer(im)

coord_list =
def calc_length(coord_list):
x0=coord_list[0][0][0]
y0=coord_list[0][0][1]
x1=coord_list[0][1][0]
y1=coord_list[0][1][1]
r = sqrt((x1-x0)**2 + (y1-y0)**2)
return r

def get_line_coord(extents):
global viewer,coord_list
coord_list.append(extents)
print('Length = ', round(calc_length(coord_list),2), ‘px’)
#close the viewer
viewer.close()

LP = dict(color=‘blue’, linestyle=’-’,
linewidth = 2, alpha=0.5)
HP = dict(color = ‘blue’,markersize = ‘10’,
marker = ‘|’,markeredgewidth = 2)

line_tool = LineTool(viewer, on_enter = get_line_coord,line_props = LP, handle_props = HP)

viewer.setWindowTitle(‘Select and drag to define cal length, then press ENTER…’)
viewer.file_menu.setHidden(True)
viewer.show()

Any help would be much appreciated. I’ve dug through the source code of the ImageViewer, but I’ve had no luck identifying the extra canvas :frowning:

Your problem might arise from outside of this code snippet’s scope. I just ran the following (had to import numpy for np.sqrt and change the formatted ’ characters) in IDLE and everything worked as you described (which is really cool!).

import numpy as np
from skimage import data
from skimage.viewer.canvastools import LineTool
from skimage.viewer import ImageViewer

im = data.camera()

viewer = ImageViewer(im)

coord_list = []
def calc_length(coord_list):
    x0=coord_list[0][0][0]
    y0=coord_list[0][0][1]
    x1=coord_list[0][1][0]
    y1=coord_list[0][1][1]
    r = np.sqrt((x1-x0)**2 + (y1-y0)**2)
    return r

def get_line_coord(extents):
    global viewer,coord_list
    coord_list.append(extents)
    print('Length = ', round(calc_length(coord_list),2), 'px')
    #close the viewer
    viewer.close()

LP = dict(color='blue', linestyle='-',
linewidth = 2, alpha=0.5)
HP = dict(color = 'blue',markersize = '10',
marker = '|',markeredgewidth = 2)

line_tool = LineTool(viewer, on_enter = get_line_coord,line_props = LP, handle_props = HP)

viewer.setWindowTitle('Select and drag to define cal length, then press ENTER…')
viewer.file_menu.setHidden(True)
viewer.show()

Thank you for such a quick reply. Very kind of you.

So when you ran this, did you also see an extra canvas pop up that would not close except manually?

Nope, I see a canvas pop up, I drag the left mouse to place the line, hit enter, the canvas is closed, and the length is returned to the IDLE console. Which editor/IDE are you using? I suspect that PyQt5 is where the error is coming up

I’m using IPython 7.12.0 in Spyder. I’ll investigate further on the pyqt5 front. Noticing issues if I try to run from conda prompt…

Gotcha, maybe it’s also a Spyder issue. I’ve done something similar:

  1. Send an image to a Qt-based viewer (napari)
  2. Do some stuff in Qt-based viewer
  3. Get results back when the viewer is closed

and the only way I could get it to work well was with a try:finally structure. That might be one way to try structuring it to see if it “cleans up” errant canvases!