diff --git a/skimage/viewer/canvastools/base.py b/skimage/viewer/canvastools/base.py index 5412198b..197800c0 100644 --- a/skimage/viewer/canvastools/base.py +++ b/skimage/viewer/canvastools/base.py @@ -33,6 +33,7 @@ class CanvasToolBase(object): self.ax = viewer.ax self.artists = [] self.active = True + viewer.add_tool(self) self.callback_on_move = _pass if on_move is None else on_move self.callback_on_enter = _pass if on_enter is None else on_enter @@ -74,6 +75,9 @@ class CanvasToolBase(object): def on_scroll(self, event): pass + def remove(self): + self.viewer.remove_tool(self) + @property def geometry(self): """Geometry information that gets passed to callback functions.""" diff --git a/skimage/viewer/tests/test_tools.py b/skimage/viewer/tests/test_tools.py index 6663c465..8cc6bd0d 100644 --- a/skimage/viewer/tests/test_tools.py +++ b/skimage/viewer/tests/test_tools.py @@ -19,7 +19,7 @@ def get_end_points(image): return np.transpose([x, y]) -def create_mouse_event(ax, button=1, xdata=0, ydata=0, key=None): +def do_event(viewer, etype, button=1, xdata=0, ydata=0, key=None): """ *name* the event name @@ -56,6 +56,7 @@ def create_mouse_event(ax, button=1, xdata=0, ydata=0, key=None): *step* number of scroll steps (positive for 'up', negative for 'down') """ + ax = viewer.ax event = namedtuple('Event', ('name canvas guiEvent x y inaxes xdata ydata ' 'button key step')) @@ -68,7 +69,9 @@ def create_mouse_event(ax, button=1, xdata=0, ydata=0, key=None): event.step = 1 event.guiEvent = None event.name = 'Custom' - return event + + func = getattr(viewer._event_manager, 'on_%s' % etype) + func(event) @skipif(not viewer_available) @@ -76,24 +79,22 @@ def test_line_tool(): img = data.camera() viewer = ImageViewer(img) - tool = LineTool(viewer.ax, maxdist=10) + tool = LineTool(viewer, maxdist=10) tool.end_points = get_end_points(img) assert_equal(tool.end_points, np.array([[170, 256], [341, 256]])) # grab a handle and move it - grab = create_mouse_event(viewer.ax, xdata=170, ydata=256) - tool.on_mouse_press(grab) - move = create_mouse_event(viewer.ax, xdata=180, ydata=260) - tool.on_move(move) - tool.on_mouse_release(move) + do_event(viewer, 'mouse_press', xdata=170, ydata=256) + do_event(viewer, 'move', xdata=180, ydata=260) + do_event(viewer, 'mouse_release') + assert_equal(tool.geometry, np.array([[180, 260], [341, 256]])) # create a new line - new = create_mouse_event(viewer.ax, xdata=10, ydata=10) - tool.on_mouse_press(new) - move = create_mouse_event(viewer.ax, xdata=100, ydata=100) - tool.on_move(move) - tool.on_mouse_release(move) + do_event(viewer, 'mouse_press', xdata=10, ydata=10) + do_event(viewer, 'move', xdata=100, ydata=100) + do_event(viewer, 'mouse_release') + assert_equal(tool.geometry, np.array([[100, 100], [10, 10]])) @@ -102,23 +103,20 @@ def test_thick_line_tool(): img = data.camera() viewer = ImageViewer(img) - tool = ThickLineTool(viewer.ax, maxdist=10) + tool = ThickLineTool(viewer, maxdist=10) tool.end_points = get_end_points(img) - scroll_up = create_mouse_event(viewer.ax, button='up') - tool.on_scroll(scroll_up) + do_event(viewer, 'scroll', button='up') assert_equal(tool.linewidth, 2) - scroll_down = create_mouse_event(viewer.ax, button='down') - tool.on_scroll(scroll_down) + do_event(viewer, 'scroll', button='down') + assert_equal(tool.linewidth, 1) - key_up = create_mouse_event(viewer.ax, key='+') - tool.on_key_press(key_up) + do_event(viewer, 'key_press', key='+') assert_equal(tool.linewidth, 2) - key_down = create_mouse_event(viewer.ax, key='-') - tool.on_key_press(key_down) + do_event(viewer, 'key_press', key='-') assert_equal(tool.linewidth, 1) @@ -127,7 +125,7 @@ def test_rect_tool(): img = data.camera() viewer = ImageViewer(img) - tool = RectangleTool(viewer.ax, maxdist=10) + tool = RectangleTool(viewer, maxdist=10) tool.extents = (100, 150, 100, 150) assert_equal(tool.corners, @@ -138,19 +136,15 @@ def test_rect_tool(): assert_equal(tool.geometry, (100, 150, 100, 150)) # grab a corner and move it - grab = create_mouse_event(viewer.ax, xdata=100, ydata=100) - tool.press(grab) - move = create_mouse_event(viewer.ax, xdata=120, ydata=120) - tool.onmove(move) - tool.release(move) + do_event(viewer, 'mouse_press', xdata=100, ydata=100) + do_event(viewer, 'move', xdata=120, ydata=120) + do_event(viewer, 'mouse_release') assert_equal(tool.geometry, [120, 150, 120, 150]) # create a new line - new = create_mouse_event(viewer.ax, xdata=10, ydata=10) - tool.press(new) - move = create_mouse_event(viewer.ax, xdata=100, ydata=100) - tool.onmove(move) - tool.release(move) + do_event(viewer, 'mouse_press', xdata=10, ydata=10) + do_event(viewer, 'move', xdata=100, ydata=100) + do_event(viewer, 'mouse_release') assert_equal(tool.geometry, [10, 100, 10, 100]) @@ -159,7 +153,7 @@ def test_paint_tool(): img = data.moon() viewer = ImageViewer(img) - tool = PaintTool(viewer.ax, img.shape) + tool = PaintTool(viewer, img.shape) tool.radius = 10 assert_equal(tool.radius, 10) @@ -167,24 +161,21 @@ def test_paint_tool(): assert_equal(tool.label, 2) assert_equal(tool.shape, img.shape) - start = create_mouse_event(viewer.ax, xdata=100, ydata=100) - tool.on_mouse_press(start) - move = create_mouse_event(viewer.ax, xdata=110, ydata=110) - tool.on_move(move) - tool.on_mouse_release(move) + do_event(viewer, 'mouse_press', xdata=100, ydata=100) + do_event(viewer, 'move', xdata=110, ydata=110) + do_event(viewer, 'mouse_release') + assert_equal(tool.overlay[tool.overlay == 2].size, 761) tool.label = 5 - start = create_mouse_event(viewer.ax, xdata=20, ydata=20) - tool.on_mouse_press(start) - move = create_mouse_event(viewer.ax, xdata=40, ydata=40) - tool.on_move(move) - tool.on_mouse_release(move) + do_event(viewer, 'mouse_press', xdata=20, ydata=20) + do_event(viewer, 'move', xdata=40, ydata=40) + do_event(viewer, 'mouse_release') + assert_equal(tool.overlay[tool.overlay == 5].size, 881) assert_equal(tool.overlay[tool.overlay == 2].size, 761) - enter = create_mouse_event(viewer.ax, key='enter') - tool.on_mouse_press(enter) + do_event(viewer, 'key_press', key='enter') tool.overlay = tool.overlay * 0 assert_equal(tool.overlay.sum(), 0) @@ -195,15 +186,14 @@ def test_base_tool(): img = data.moon() viewer = ImageViewer(img) - tool = CanvasToolBase(viewer.ax) + tool = CanvasToolBase(viewer) tool.set_visible(False) tool.set_visible(True) - enter = create_mouse_event(viewer.ax, key='enter') - tool._on_key_press(enter) + do_event(viewer, 'key_press', key='enter') tool.redraw() tool.remove() - tool = CanvasToolBase(viewer.ax, useblit=False) + tool = CanvasToolBase(viewer, useblit=False) tool.redraw() diff --git a/skimage/viewer/tests/test_utils.py b/skimage/viewer/tests/test_utils.py index 3f9ec396..77f30299 100644 --- a/skimage/viewer/tests/test_utils.py +++ b/skimage/viewer/tests/test_utils.py @@ -28,7 +28,7 @@ def test_format_filename(): def test_open_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() - timer.singleShot(10, QtGui.QApplication.quit) + timer.singleShot(100, lambda: QtGui.QApplication.quit()) filename = dialogs.open_file_dialog() assert filename is None @@ -37,6 +37,6 @@ def test_open_file_dialog(): def test_save_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() - timer.singleShot(10, QtGui.QApplication.quit) + timer.singleShot(100, lambda: QtGui.QApplication.quit()) filename = dialogs.save_file_dialog() assert filename is None diff --git a/skimage/viewer/tests/test_widgets.py b/skimage/viewer/tests/test_widgets.py index ef3cb83c..ee4c91fb 100644 --- a/skimage/viewer/tests/test_widgets.py +++ b/skimage/viewer/tests/test_widgets.py @@ -77,11 +77,11 @@ def test_save_buttons(): viewer.plugins[0] += sv import tempfile - _, filename = tempfile.mkstemp(suffix='.png') - os.remove(filename) + fid, filename = tempfile.mkstemp(suffix='.png') + os.close(fid) timer = QtCore.QTimer() - timer.singleShot(100, lambda: QtGui.QApplication.quit()) + timer.singleShot(100, QtGui.QApplication.quit) sv.save_to_stack() sv.save_to_file(filename) @@ -92,6 +92,8 @@ def test_save_buttons(): img = io.pop() assert_almost_equal(img, viewer.image) + os.remove(filename) + @skipif(not viewer_available) def test_ok_buttons(): diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index f1e541a1..00902112 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -60,6 +60,14 @@ class BlitManager(object): self.background = None self.artists = [] + def add_artists(self, artists): + self.artists.extend(artists) + self.redraw() + + def remove_artists(self, artists): + for artist in artists: + self.artist.remove(artist) + def on_draw_event(self, event=None): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.draw_artists() @@ -95,6 +103,13 @@ class EventManager(object): self.tools.append(tool) self.active_tool = tool + def detach(self, tool): + self.tools.remove(tool) + if self.tools: + self.active_tool = self.tools[-1] + else: + self.active_tool = None + def on_mouse_press(self, event): for tool in self.tools: if not tool.ignore(event) and tool.hit_test(event): @@ -375,10 +390,16 @@ class ImageViewer(QtGui.QMainWindow): def add_tool(self, tool): if self.useblit: - self._blit_manager.artists.extend(tool.artists) + self._blit_manager.add_artists(tool.artists) self._tools.append(tool) self._event_manager.attach(tool) + def remove_tool(self, tool): + if self.useblit: + self._blit_manager.remove_artists(tool.artists) + self._tools.remove(tool) + self._event_manager.detach(tool) + def _format_coord(self, x, y): # callback function to format coordinate display in status bar x = int(x + 0.5)