diff --git a/skimage/viewer/canvastools/base.py b/skimage/viewer/canvastools/base.py index 6fcda9c4..7f61ce2c 100644 --- a/skimage/viewer/canvastools/base.py +++ b/skimage/viewer/canvastools/base.py @@ -115,7 +115,7 @@ class CanvasToolBase(object): @property def geometry(self): """Geometry information that gets passed to callback functions.""" - raise NotImplementedError + return None class ToolHandles(object): diff --git a/skimage/viewer/canvastools/linetool.py b/skimage/viewer/canvastools/linetool.py index c32d0ea7..fed18383 100644 --- a/skimage/viewer/canvastools/linetool.py +++ b/skimage/viewer/canvastools/linetool.py @@ -69,7 +69,7 @@ class LineTool(CanvasToolBase): @property def end_points(self): - return self._end_pts + return self._end_pts.astype(int) @end_points.setter def end_points(self, pts): @@ -191,7 +191,7 @@ class ThickLineTool(LineTool): self.callback_on_change(self.geometry) -if __name__ == '__main__': +if __name__ == '__main__': # pragma: no cover import matplotlib.pyplot as plt from skimage import data diff --git a/skimage/viewer/canvastools/painttool.py b/skimage/viewer/canvastools/painttool.py index 66cec08d..6af8b5d3 100644 --- a/skimage/viewer/canvastools/painttool.py +++ b/skimage/viewer/canvastools/painttool.py @@ -198,7 +198,7 @@ class CenteredWindow(object): return [slice(ymin, ymax), slice(xmin, xmax)] -if __name__ == '__main__': +if __name__ == '__main__': # pragma: no cover np.testing.rundocs() from skimage import data diff --git a/skimage/viewer/canvastools/recttool.py b/skimage/viewer/canvastools/recttool.py index d4ae113e..f055e439 100644 --- a/skimage/viewer/canvastools/recttool.py +++ b/skimage/viewer/canvastools/recttool.py @@ -201,7 +201,7 @@ class RectangleTool(CanvasToolBase, RectangleSelector): return self.extents -if __name__ == '__main__': +if __name__ == '__main__': # pragma: no cover import matplotlib.pyplot as plt from skimage import data diff --git a/skimage/viewer/plugins/base.py b/skimage/viewer/plugins/base.py index baded1cb..6b162fd8 100644 --- a/skimage/viewer/plugins/base.py +++ b/skimage/viewer/plugins/base.py @@ -239,7 +239,8 @@ class Plugin(QtGui.QDialog): def clean_up(self): self.remove_image_artists() - self.image_viewer.plugins.remove(self) + if self in self.image_viewer.plugins: + self.image_viewer.plugins.remove(self) self.image_viewer.reset_image() self.image_viewer.redraw() diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index d0011c75..6e5b2c1f 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -46,7 +46,7 @@ class LineProfile(PlotPlugin): if self._limit_type == 'image': self.limits = (np.min(image), np.max(image)) elif self._limit_type == 'dtype': - self._limit_type = dtype_range[image.dtype.type] + self.limits = dtype_range[image.dtype.type] elif self._limit_type is None or len(self._limit_type) == 2: self.limits = self._limit_type else: @@ -91,6 +91,7 @@ class LineProfile(PlotPlugin): profile: list of 1d arrays Profile of intensity values. Length 1 (grayscale) or 3 (rgb). """ + self._update_data() profiles = [data.get_ydata() for data in self.profile] return self.line_tool.end_points, profiles @@ -103,8 +104,15 @@ class LineProfile(PlotPlugin): def line_changed(self, end_points): x, y = np.transpose(end_points) self.line_tool.end_points = end_points - scan = measure.profile_line(self.image_viewer.original_image, - *end_points[:, ::-1], + self._update_data() + self.ax.relim() + + self._autoscale_view() + self.redraw() + + def _update_data(self): + scan = measure.profile_line(self.image_viewer.image, + *self.line_tool.end_points[:, ::-1], linewidth=self.line_tool.linewidth) self.scan_data = scan if scan.ndim == 1: @@ -117,11 +125,6 @@ class LineProfile(PlotPlugin): self.profile[i].set_xdata(np.arange(scan.shape[0])) self.profile[i].set_ydata(scan[:, i]) - self.ax.relim() - - self._autoscale_view() - self.redraw() - def reset_axes(self, scan_data): # Clear lines out for line in self.ax.lines: @@ -147,7 +150,7 @@ class LineProfile(PlotPlugin): The line scan values across the image. """ end_points = self.line_tool.end_points - line_image = np.zeros(self.image_viewer.original_image.shape[:2], + line_image = np.zeros(self.image_viewer.image.shape[:2], np.uint8) width = self.line_tool.linewidth if width > 1: @@ -162,4 +165,3 @@ class LineProfile(PlotPlugin): rr, cc = draw.line(y1, x1, y2, x2) line_image[rr, cc] = 255 return line_image, self.scan_data - diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py new file mode 100644 index 00000000..7ef62730 --- /dev/null +++ b/skimage/viewer/tests/test_plugins.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +import numpy as np +import skimage +import skimage.data as data +from skimage.filter.rank import median +from skimage.morphology import disk +from numpy.testing import assert_equal, assert_allclose, assert_almost_equal +from numpy.testing.decorators import skipif + +try: + from skimage.viewer.qt import qt_api + from skimage.viewer import ImageViewer + from skimage.viewer.plugins import ( + LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, + PlotPlugin) + from skimage.viewer.plugins.base import Plugin + from skimage.viewer.widgets import Slider + viewer_available = not qt_api is None +except ImportError: + viewer_available = False + + +def setup_line_profile(image, limits='image'): + viewer = ImageViewer(skimage.img_as_float(image)) + plugin = LineProfile(limits=limits) + viewer += plugin + return plugin + + +@skipif(not viewer_available) +def test_line_profile(): + """ Test a line profile using an ndim=2 image""" + plugin = setup_line_profile(data.camera()) + line_image, scan_data = plugin.output() + for inp in [line_image.nonzero()[0].size, + line_image.sum() / line_image.max(), + scan_data.size]: + assert_equal(inp, 172) + assert_equal(line_image.shape, (512, 512)) + assert_allclose(scan_data.max(), 0.9176, rtol=1e-3) + assert_allclose(scan_data.mean(), 0.2812, rtol=1e-3) + + +@skipif(not viewer_available) +def test_line_profile_rgb(): + """ Test a line profile using an ndim=3 image""" + plugin = setup_line_profile(data.chelsea(), limits=None) + for i in range(6): + plugin.line_tool._thicken_scan_line() + line_image, scan_data = plugin.output() + assert_equal(line_image[line_image == 128].size, 750) + assert_equal(line_image[line_image == 255].size, 151) + assert_equal(line_image.shape, (300, 451)) + assert_equal(scan_data.shape, (151, 3)) + assert_allclose(scan_data.max(), 0.772, rtol=1e-3) + assert_allclose(scan_data.mean(), 0.4359, rtol=1e-3) + + +@skipif(not viewer_available) +def test_line_profile_dynamic(): + """Test a line profile updating after an image transform""" + image = data.coins()[:-50, :] # shave some off to make the line lower + image = skimage.img_as_float(image) + viewer = ImageViewer(image) + + lp = LineProfile(limits='dtype') + viewer += lp + + line = lp.get_profiles()[-1][0] + assert line.size == 129 + assert_almost_equal(np.std(viewer.image), 0.208, 3) + assert_almost_equal(np.std(line), 0.229, 3) + assert_almost_equal(np.max(line) - np.min(line), 0.725, 1) + + viewer.image = skimage.img_as_float(median(image, + selem=disk(radius=3))) + + line = lp.get_profiles()[-1][0] + assert_almost_equal(np.std(viewer.image), 0.198, 3) + assert_almost_equal(np.std(line), 0.220, 3) + assert_almost_equal(np.max(line) - np.min(line), 0.639, 1) + + +@skipif(not viewer_available) +def test_measure(): + image = data.camera() + viewer = ImageViewer(image) + m = Measure() + viewer += m + + m.line_changed([(0, 0), (10, 10)]) + assert_equal(str(m._length.text), '14.1') + assert_equal(str(m._angle.text[:5]), '135.0') + + +@skipif(not viewer_available) +def test_canny(): + image = data.camera() + viewer = ImageViewer(image) + c = CannyPlugin() + viewer += c + + canny_edges = viewer.show(False) + viewer.close() + edges = canny_edges[0][0] + assert edges.sum() == 2852 + + +@skipif(not viewer_available) +def test_label_painter(): + image = data.camera() + moon = data.moon() + viewer = ImageViewer(image) + lp = LabelPainter() + viewer += lp + + assert_equal(lp.radius, 5) + lp.label = 1 + assert_equal(str(lp.label), '1') + lp.label = 2 + assert_equal(str(lp.paint_tool.label), '2') + assert_equal(lp.paint_tool.radius, 5) + lp._on_new_image(moon) + assert_equal(lp.paint_tool.shape, moon.shape) + + +@skipif(not viewer_available) +def test_crop(): + image = data.camera() + viewer = ImageViewer(image) + c = Crop() + viewer += c + + c.crop((0, 100, 0, 100)) + assert_equal(viewer.image.shape, (101, 101)) + + +@skipif(not viewer_available) +def test_color_histogram(): + image = skimage.img_as_float(data.load('color.png')) + viewer = ImageViewer(image) + ch = ColorHistogram(dock='right') + viewer += ch + + assert_almost_equal(viewer.image.std(), 0.352, 3), + ch.ab_selected((0, 100, 0, 100)), + assert_almost_equal(viewer.image.std(), 0.325, 3) + + +@skipif(not viewer_available) +def test_plot_plugin(): + viewer = ImageViewer(data.moon()) + plugin = PlotPlugin(image_filter=lambda x: x) + viewer += plugin + + assert_equal(viewer.image, data.moon()) + plugin._update_original_image(data.coins()) + assert_equal(viewer.image, data.coins()) + viewer.close() + + +@skipif(not viewer_available) +def test_plugin(): + img = skimage.img_as_float(data.moon()) + viewer = ImageViewer(img) + + def median_filter(img, radius=3): + return median(img, selem=disk(radius=radius)) + + plugin = Plugin(image_filter=median_filter) + viewer += plugin + + plugin += Slider('radius', 1, 5) + + assert_almost_equal(np.std(viewer.image), 12.556, 3) + + plugin.filter_image() + + assert_almost_equal(np.std(viewer.image), 12.931, 3) + + plugin.show() + plugin.close() + plugin.clean_up() + img, _ = plugin.output() + assert_equal(img, viewer.image) diff --git a/skimage/viewer/tests/test_tools.py b/skimage/viewer/tests/test_tools.py new file mode 100644 index 00000000..9f02e762 --- /dev/null +++ b/skimage/viewer/tests/test_tools.py @@ -0,0 +1,213 @@ +from collections import namedtuple + +import numpy as np +from skimage import data +from numpy.testing import assert_equal +from numpy.testing.decorators import skipif + +try: + from skimage.viewer.qt import qt_api + from skimage.viewer import ImageViewer + from skimage.viewer.canvastools import ( + LineTool, ThickLineTool, RectangleTool, PaintTool) + from skimage.viewer.canvastools.base import CanvasToolBase + viewer_available = not qt_api is None +except ImportError: + viewer_available = False + + +def get_end_points(image): + h, w = image.shape[0:2] + x = [w / 3, 2 * w / 3] + y = [h / 2] * 2 + return np.transpose([x, y]) + + +def create_mouse_event(ax, button=1, xdata=0, ydata=0, key=None): + """ + *name* + the event name + + *canvas* + the FigureCanvas instance generating the event + + *guiEvent* + the GUI event that triggered the matplotlib event + + *x* + x position - pixels from left of canvas + + *y* + y position - pixels from bottom of canvas + + *inaxes* + the :class:`~matplotlib.axes.Axes` instance if mouse is over axes + + *xdata* + x coord of mouse in data coords + + *ydata* + y coord of mouse in data coords + + *button* + button pressed None, 1, 2, 3, 'up', 'down' (up and down are used + for scroll events) + + *key* + the key depressed when the mouse event triggered (see + :class:`KeyEvent`) + + *step* + number of scroll steps (positive for 'up', negative for 'down') + """ + event = namedtuple('Event', + ('name canvas guiEvent x y inaxes xdata ydata ' + 'button key step')) + event.button = button + event.x, event.y = ax.transData.transform((xdata, ydata)) + event.xdata, event.ydata = xdata, ydata + event.inaxes = ax + event.canvas = ax.figure.canvas + event.key = key + event.step = 1 + event.guiEvent = None + event.name = 'Custom' + return event + + +@skipif(not viewer_available) +def test_line_tool(): + img = data.camera() + viewer = ImageViewer(img) + + tool = LineTool(viewer.ax, 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) + 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) + assert_equal(tool.geometry, np.array([[100, 100], [10, 10]])) + + +@skipif(not viewer_available) +def test_thick_line_tool(): + img = data.camera() + viewer = ImageViewer(img) + + tool = ThickLineTool(viewer.ax, maxdist=10) + tool.end_points = get_end_points(img) + + scroll_up = create_mouse_event(viewer.ax, button='up') + tool.on_scroll(scroll_up) + assert_equal(tool.linewidth, 2) + + scroll_down = create_mouse_event(viewer.ax, button='down') + tool.on_scroll(scroll_down) + assert_equal(tool.linewidth, 1) + + key_up = create_mouse_event(viewer.ax, key='+') + tool.on_key_press(key_up) + assert_equal(tool.linewidth, 2) + + key_down = create_mouse_event(viewer.ax, key='-') + tool.on_key_press(key_down) + assert_equal(tool.linewidth, 1) + + +@skipif(not viewer_available) +def test_rect_tool(): + img = data.camera() + viewer = ImageViewer(img) + + tool = RectangleTool(viewer.ax, maxdist=10) + tool.extents = (100, 150, 100, 150) + + assert_equal(tool.corners, + ((100, 150, 150, 100), (100, 100, 150, 150))) + assert_equal(tool.extents, (100, 150, 100, 150)) + assert_equal(tool.edge_centers, + ((100, 125.0, 150, 125.0), (125.0, 100, 125.0, 150))) + 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) + 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) + assert_equal(tool.geometry, [10, 100, 10, 100]) + + +@skipif(not viewer_available) +def test_paint_tool(): + img = data.moon() + viewer = ImageViewer(img) + + tool = PaintTool(viewer.ax, img.shape) + + tool.radius = 10 + assert_equal(tool.radius, 10) + tool.label = 2 + 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) + 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) + 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) + + tool.overlay = tool.overlay * 0 + assert_equal(tool.overlay.sum(), 0) + + +@skipif(not viewer_available) +def test_base_tool(): + img = data.moon() + viewer = ImageViewer(img) + + tool = CanvasToolBase(viewer.ax) + tool.set_visible(False) + tool.set_visible(True) + + enter = create_mouse_event(viewer.ax, key='enter') + tool._on_key_press(enter) + + tool.redraw() + tool.remove() + + tool = CanvasToolBase(viewer.ax, useblit=False) + tool.redraw() diff --git a/skimage/viewer/tests/test_utils.py b/skimage/viewer/tests/test_utils.py new file mode 100644 index 00000000..30d74a51 --- /dev/null +++ b/skimage/viewer/tests/test_utils.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +from numpy.testing.decorators import skipif + +try: + from skimage.viewer.qt import qt_api, QtCore, QtGui + from skimage.viewer import utils + from skimage.viewer.utils import dialogs +except ImportError: + qt_api = None + + +@skipif(qt_api is None) +def test_event_loop(): + utils.init_qtapp() + timer = QtCore.QTimer() + timer.singleShot(10, QtGui.QApplication.quit) + utils.start_qtapp() + + +@skipif(qt_api is None) +def test_format_filename(): + fname = dialogs._format_filename(('apple', 2)) + assert fname == 'apple' + fname = dialogs._format_filename('') + assert fname is None + + +@skipif(qt_api is None) +def test_open_file_dialog(): + utils.init_qtapp() + timer = QtCore.QTimer() + timer.singleShot(10, QtGui.QApplication.quit) + filename = dialogs.open_file_dialog() + assert filename is None + + +@skipif(qt_api is None) +def test_save_file_dialog(): + utils.init_qtapp() + timer = QtCore.QTimer() + timer.singleShot(10, QtGui.QApplication.quit) + filename = dialogs.save_file_dialog() + assert filename is None diff --git a/skimage/viewer/tests/test_viewer.py b/skimage/viewer/tests/test_viewer.py index 7fa4e374..cd6c7acb 100644 --- a/skimage/viewer/tests/test_viewer.py +++ b/skimage/viewer/tests/test_viewer.py @@ -1,48 +1,83 @@ -import skimage -import skimage.data as data -from skimage.viewer import ImageViewer -from skimage.viewer.qt import qt_api -from numpy.testing import assert_equal, assert_allclose + +from skimage import data +from skimage.transform import pyramid_gaussian +from skimage.filter import sobel +from numpy.testing import assert_equal from numpy.testing.decorators import skipif - -def setup_line_profile(image): - from skimage.viewer.plugins.lineprofile import LineProfile - viewer = ImageViewer(skimage.img_as_float(image)) - plugin = LineProfile() - viewer += plugin - return plugin +try: + from skimage.viewer.qt import qt_api, QtGui, QtCore + from skimage.viewer.plugins import OverlayPlugin + from skimage.viewer.plugins.overlayplugin import recent_mpl_version + from skimage.viewer import ImageViewer, CollectionViewer + viewer_available = not qt_api is None +except ImportError: + viewer_available = False -@skipif(qt_api is None) -def test_line_profile(): - """ Test a line profile using an ndim=2 image""" - plugin = setup_line_profile(data.camera()) - line_image, scan_data = plugin.output() - for inp in [line_image.nonzero()[0].size, - line_image.sum() / line_image.max(), - scan_data.size]: - assert_equal(inp, 172) - assert_equal(line_image.shape, (512, 512)) - assert_allclose(scan_data.max(), 0.9139, rtol=1e-3) - assert_allclose(scan_data.mean(), 0.2828, rtol=1e-3) +@skipif(not viewer_available) +def test_viewer(): + lena = data.lena() + coins = data.coins() + + view = ImageViewer(lena) + import tempfile + _, filename = tempfile.mkstemp(suffix='.png') + + view.show(False) + view.close() + view.save_to_file(filename) + view.open_file(filename) + assert_equal(view.image, lena) + view.image = coins + assert_equal(view.image, coins), + view.save_to_file(filename), + view.open_file(filename), + view.reset_image(), + assert_equal(view.image, coins) -@skipif(qt_api is None) -def test_line_profile_rgb(): - """ Test a line profile using an ndim=3 image""" - plugin = setup_line_profile(data.chelsea()) - for i in range(6): - plugin.line_tool._thicken_scan_line() - line_image, scan_data = plugin.output() - assert_equal(line_image[line_image == 128].size, 755) - assert_equal(line_image[line_image == 255].size, 151) - assert_equal(line_image.shape, (300, 451)) - assert_equal(scan_data.shape, (152, 3)) - assert_allclose(scan_data.max(), 0.772, rtol=1e-3) - assert_allclose(scan_data.mean(), 0.4355, rtol=1e-3) +def make_key_event(key): + return QtGui.QKeyEvent(QtCore.QEvent.KeyPress, key, + QtCore.Qt.NoModifier) + + +@skipif(not viewer_available) +def test_collection_viewer(): + + img = data.lena() + img_collection = tuple(pyramid_gaussian(img)) + + view = CollectionViewer(img_collection) + make_key_event(48) + + view.update_index('', 2), + assert_equal(view.image, img_collection[2]) + view.keyPressEvent(make_key_event(53)) + assert_equal(view.image, img_collection[5]) + view._format_coord(10, 10) + + +@skipif(not viewer_available or not recent_mpl_version()) +def test_viewer_with_overlay(): + img = data.coins() + ov = OverlayPlugin(image_filter=sobel) + viewer = ImageViewer(img) + viewer += ov + + import tempfile + _, filename = tempfile.mkstemp(suffix='.png') + + ov.color = 2 + assert_equal(ov.color, 'yellow') + viewer.save_to_file(filename) + ov.display_filtered_image(img) + assert_equal(ov.overlay, img) + ov.overlay = None + assert_equal(ov.overlay, None) + ov.overlay = img + assert_equal(ov.overlay, img) + assert_equal(ov.filtered_image, img) + -if __name__ == "__main__": - from numpy.testing import run_module_suite - run_module_suite() diff --git a/skimage/viewer/tests/test_widgets.py b/skimage/viewer/tests/test_widgets.py new file mode 100644 index 00000000..aeb4ede2 --- /dev/null +++ b/skimage/viewer/tests/test_widgets.py @@ -0,0 +1,109 @@ + +import os +from skimage import data, img_as_float, io +from numpy.testing import assert_almost_equal, assert_equal +from numpy.testing.decorators import skipif + +try: + from skimage.viewer.qt import qt_api, QtGui, QtCore + from skimage.viewer import ImageViewer + from skimage.viewer.widgets import ( + Slider, OKCancelButtons, SaveButtons, ComboBox, Text) + from skimage.viewer.plugins.base import Plugin + viewer_available = not qt_api is None +except ImportError: + viewer_available = False + + +def get_image_viewer(): + image = data.coins() + viewer = ImageViewer(img_as_float(image)) + viewer += Plugin() + return viewer + + +@skipif(not viewer_available) +def test_combo_box(): + viewer = get_image_viewer() + cb = ComboBox('hello', ('a', 'b', 'c')) + viewer.plugins[0] += cb + + assert_equal(str(cb.val), 'a') + assert_equal(cb.index, 0) + cb.index = 2 + assert_equal(str(cb.val), 'c'), + assert_equal(cb.index, 2) + + +@skipif(not viewer_available) +def test_text_widget(): + viewer = get_image_viewer() + txt = Text('hello', 'hello, world!') + viewer.plugins[0] += txt + + assert_equal(str(txt.text), 'hello, world!') + txt.text = 'goodbye, world!' + assert_equal(str(txt.text), 'goodbye, world!') + + +@skipif(not viewer_available) +def test_slider_int(): + viewer = get_image_viewer() + sld = Slider('radius', 2, 10, value_type='int') + viewer.plugins[0] += sld + + assert_equal(sld.val, 4) + sld.val = 6 + assert_equal(sld.val, 6) + sld.editbox.setText('5') + sld._on_editbox_changed() + assert_equal(sld.val, 5) + + +@skipif(not viewer_available) +def test_slider_float(): + viewer = get_image_viewer() + sld = Slider('alpha', 2.1, 3.1, value=2.1, value_type='float', + orientation='vertical', update_on='move') + viewer.plugins[0] += sld + + assert_equal(sld.val, 2.1) + sld.val = 2.5 + assert_almost_equal(sld.val, 2.5, 2) + sld.editbox.setText('0.1') + sld._on_editbox_changed() + assert_almost_equal(sld.val, 2.5, 2) + + +@skipif(not viewer_available) +def test_save_buttons(): + viewer = get_image_viewer() + sv = SaveButtons() + viewer.plugins[0] += sv + + import tempfile + _, filename = tempfile.mkstemp(suffix='.png') + os.remove(filename) + + timer = QtCore.QTimer() + timer.singleShot(100, lambda: QtGui.QApplication.quit()) + + sv.save_to_stack() + sv.save_to_file(filename) + + img = img_as_float(data.imread(filename)) + assert_almost_equal(img, viewer.image) + + img = io.pop() + assert_almost_equal(img, viewer.image) + + +@skipif(not viewer_available) +def test_ok_buttons(): + viewer = get_image_viewer() + ok = OKCancelButtons() + viewer.plugins[0] += ok + + ok.update_original_image(), + ok.close_plugin() + diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 32c10cd4..476017ef 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -174,9 +174,10 @@ class ImageViewer(QtGui.QMainWindow): h = viewer_size.height() self.resize(w + dx, h + dy) - def open_file(self): + def open_file(self, filename=None): """Open image file and display in viewer.""" - filename = dialogs.open_file_dialog() + if filename is None: + filename = dialogs.open_file_dialog() if filename is None: return image = io.imread(filename) @@ -187,7 +188,7 @@ class ImageViewer(QtGui.QMainWindow): self.image = image.copy() # update displayed image self.original_image_changed.emit(image) - def save_to_file(self): + def save_to_file(self, filename=None): """Save current image to file. The current behavior is not ideal: It saves the image displayed on @@ -195,7 +196,8 @@ class ImageViewer(QtGui.QMainWindow): not preserved (resizing the viewer window will alter the size of the saved image). """ - filename = dialogs.save_file_dialog() + if filename is None: + filename = dialogs.save_file_dialog() if filename is None: return if len(self.ax.images) == 1: diff --git a/skimage/viewer/widgets/core.py b/skimage/viewer/widgets/core.py index 2bbf53d2..74fe9de4 100644 --- a/skimage/viewer/widgets/core.py +++ b/skimage/viewer/widgets/core.py @@ -244,7 +244,7 @@ class ComboBox(BaseWidget): @property def val(self): - return self._combo_box.value() + return self._combo_box.currentText() @property def index(self): diff --git a/skimage/viewer/widgets/history.py b/skimage/viewer/widgets/history.py index ce20b559..d1df8689 100644 --- a/skimage/viewer/widgets/history.py +++ b/skimage/viewer/widgets/history.py @@ -81,8 +81,9 @@ class SaveButtons(BaseWidget): NOTE: The io stack only works in interactive sessions.''') notify(msg) - def save_to_file(self): - filename = dialogs.save_file_dialog() + def save_to_file(self, filename=None): + if filename is None: + filename = dialogs.save_file_dialog() if filename is None: return image = self.plugin.filtered_image