diff --git a/skimage/viewer/plugins/base.py b/skimage/viewer/plugins/base.py index aa6b585a..ab18d257 100644 --- a/skimage/viewer/plugins/base.py +++ b/skimage/viewer/plugins/base.py @@ -1,9 +1,12 @@ """ Base class for Plugins that interact with ImageViewer. """ -from ..qt import QtGui -from ..qt.QtCore import Qt +from warnings import warn +import numpy as np + +from ..qt import QtGui +from ..qt.QtCore import Qt, pyqtSignal from ..utils import RequiredAttr, init_qtapp @@ -71,6 +74,10 @@ class Plugin(QtGui.QDialog): name = 'Plugin' image_viewer = RequiredAttr("%s is not attached to ImageViewer" % name) + # Signals used when viewers are linked to the Plugin output. + image_updated = pyqtSignal(np.ndarray) + _started = pyqtSignal() + def __init__(self, image_filter=None, height=0, width=400, useblit=True): init_qtapp() super(Plugin, self).__init__() @@ -79,6 +86,9 @@ class Plugin(QtGui.QDialog): # If subclass defines `image_filter` method ignore input. if not hasattr(self, 'image_filter'): self.image_filter = image_filter + elif image_filter is not None: + warn("If the Plugin class defines an `image_filter` method, " + "then the `image_filter` argument is ignored.") self.setWindowTitle(self.name) self.layout = QtGui.QGridLayout(self) @@ -155,7 +165,9 @@ class Plugin(QtGui.QDialog): kwargs = dict([(name, self._get_value(a)) for name, a in self.keyword_arguments.iteritems()]) filtered = self.image_filter(*arguments, **kwargs) + self.display_filtered_image(filtered) + self.image_updated.emit(filtered) def _get_value(self, param): # If param is a widget, return its `val` attribute. @@ -183,6 +195,11 @@ class Plugin(QtGui.QDialog): """ setattr(self, name, value) + def show(self, main_window=True): + """Show plugin.""" + super(Plugin, self).show() + self._started.emit() + def closeEvent(self, event): """On close disconnect all artists and events from ImageViewer. diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 9d6261c6..209e6b42 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -11,6 +11,7 @@ import numpy as np from .. import utils from ..widgets import Slider from ..utils import dialogs +from ..plugins.base import Plugin __all__ = ['ImageViewer', 'CollectionViewer'] @@ -81,6 +82,13 @@ class ImageViewer(QtGui.QMainWindow): self.main_widget = QtGui.QWidget() self.setCentralWidget(self.main_widget) + if isinstance(image, Plugin): + plugin = image + image = plugin.filtered_image + plugin.image_updated.connect(self._new_original_image) + # When plugin is started, start + plugin._started.connect(self._show) + self.fig, self.ax = utils.figimage(image) self.canvas = self.fig.canvas self.canvas.setParent(self) @@ -88,9 +96,7 @@ class ImageViewer(QtGui.QMainWindow): self.ax.autoscale(enable=False) self._image_plot = self.ax.images[0] - - self.original_image = image - self.image = image.copy() + self._new_original_image(image) self.plugins = [] self.layout = QtGui.QVBoxLayout(self.main_widget) @@ -115,8 +121,11 @@ class ImageViewer(QtGui.QMainWindow): if filename is None: return image = io.imread(filename) + self._new_original_image(image) + + def _new_original_image(self, image): self.original_image = image # update saved image - self.image = image # update displayed image + self.image = image.copy() # update displayed image def save_to_file(self): """Save current image to file. @@ -160,16 +169,20 @@ class ImageViewer(QtGui.QMainWindow): p.move(w, y) y += p.geometry().height() - def show(self): - """Show ImageViewer and attached plugins. - - This behaves much like `matplotlib.pyplot.show` and `QWidget.show`. - """ + def _show(self): self.auto_layout() for p in self.plugins: p.show() super(ImageViewer, self).show() - utils.start_qtapp() + + def show(self, main_window=True): + """Show ImageViewer and attached plugins. + + This behaves much like `matplotlib.pyplot.show` and `QWidget.show`. + """ + self._show() + if main_window: + utils.start_qtapp() def redraw(self): self.canvas.draw_idle()