mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-15 11:25:53 +08:00
Add PlotPlugin and cleanup code.
This commit is contained in:
+126
-10
@@ -1,7 +1,31 @@
|
||||
from PyQt4 import QtGui
|
||||
|
||||
import numpy as np
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
|
||||
from skimage.io._plugins.q_color_mixer import IntelligentSlider
|
||||
|
||||
|
||||
class PlotCanvas(FigureCanvasQTAgg):
|
||||
"""Canvas for displaying images.
|
||||
|
||||
This canvas derives from Matplotlib, and has attributes `fig` and `ax`,
|
||||
which point to Matplotlib figure and axes.
|
||||
"""
|
||||
def __init__(self, parent, height, width, **kwargs):
|
||||
print height, width
|
||||
self.fig, self.ax = plt.subplots(figsize=(height, width), **kwargs)
|
||||
|
||||
FigureCanvasQTAgg.__init__(self, self.fig)
|
||||
FigureCanvasQTAgg.setSizePolicy(self,
|
||||
QtGui.QSizePolicy.Expanding,
|
||||
QtGui.QSizePolicy.Expanding)
|
||||
FigureCanvasQTAgg.updateGeometry(self)
|
||||
# Note: `setParent` must be called after `FigureCanvasQTAgg.__init__`.
|
||||
self.setParent(parent)
|
||||
|
||||
|
||||
class Plugin(QtGui.QDialog):
|
||||
"""Base class for widgets that interact with the axes.
|
||||
|
||||
@@ -12,24 +36,22 @@ class Plugin(QtGui.QDialog):
|
||||
useblit : bool
|
||||
If True, use blitting to speed up animation. Only available on some
|
||||
backends. If None, set to True when using Agg backend, otherwise False.
|
||||
figure : :class:`~matplotlib.figure.Figure`
|
||||
If None, create a figure with a single axes.
|
||||
no_toolbar : bool
|
||||
If True, figure created by plugin has no toolbar. This has no effect
|
||||
on figures passed into `Plugin`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
viewer : ImageViewer
|
||||
image_viewer : ImageViewer
|
||||
Window containing image used in measurement.
|
||||
image : array
|
||||
Image used in measurement/manipulation.
|
||||
overlay : array
|
||||
Image used in measurement/manipulation.
|
||||
"""
|
||||
def __init__(self, image_viewer, callback=None, height=100, width=400):
|
||||
self._viewer = image_viewer
|
||||
def __init__(self, image_viewer, callback=None, height=100, width=400,
|
||||
useblit=None):
|
||||
self.image_viewer = image_viewer
|
||||
QtGui.QDialog.__init__(self, image_viewer)
|
||||
self.image_viewer.plugins.append(self)
|
||||
|
||||
self.setWindowTitle('Image Plugin')
|
||||
self.layout = QtGui.QGridLayout(self)
|
||||
self.resize(width, height)
|
||||
@@ -40,8 +62,17 @@ class Plugin(QtGui.QDialog):
|
||||
self.arguments = [image_viewer.original_image]
|
||||
self.keyword_arguments= {}
|
||||
|
||||
self.overlay = self._viewer.overlay
|
||||
self.image = self._viewer.image
|
||||
self.overlay = self.image_viewer.overlay
|
||||
self.image = self.image_viewer.image
|
||||
|
||||
|
||||
if useblit is None:
|
||||
useblit = True if mpl.backends.backend.endswith('Agg') else False
|
||||
self.useblit = useblit
|
||||
self.cids = []
|
||||
self.artists = []
|
||||
|
||||
self.connect_event('draw_event', self.on_draw)
|
||||
|
||||
def caller(self, *args):
|
||||
arguments = [self._get_value(a) for a in self.arguments]
|
||||
@@ -69,3 +100,88 @@ class Plugin(QtGui.QDialog):
|
||||
self.layout.addWidget(slider, self.row, 0)
|
||||
self.row += 1
|
||||
return name.replace(' ', '_'), slider
|
||||
|
||||
def redraw(self):
|
||||
self.canvas.draw_idle()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Disconnect all artists and events from ImageViewer.
|
||||
|
||||
Note that events must be connected using `self.connect_event` and
|
||||
artists must be appended to `self.artists`.
|
||||
"""
|
||||
self.disconnect_image_events()
|
||||
self.remove_artists()
|
||||
self.image_viewer.plugins.remove(self)
|
||||
self.image_viewer.redraw()
|
||||
self.close()
|
||||
|
||||
def connect_event(self, event, callback):
|
||||
"""Connect callback with an event.
|
||||
|
||||
This should be used in lieu of `figure.canvas.mpl_connect` since this
|
||||
function stores call back ids for later clean up.
|
||||
"""
|
||||
cid = self.image_viewer.connect_event(event, callback)
|
||||
self.cids.append(cid)
|
||||
|
||||
def disconnect_image_events(self):
|
||||
"""Disconnect all events created by this widget."""
|
||||
for c in self.cids:
|
||||
self.image_viewer.disconnect_event(c)
|
||||
|
||||
def remove_artists(self):
|
||||
"""Disconnect artists that are connected to the *image plot*."""
|
||||
for a in self.artists:
|
||||
self.image_viewer.remove_artist(a)
|
||||
|
||||
|
||||
class PlotPlugin(Plugin):
|
||||
"""Plugin for ImageViewer that contains a plot Canvas.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image_viewer : ImageViewer instance.
|
||||
Window containing image used in measurement/manipulation.
|
||||
figure : :class:`~matplotlib.figure.Figure`
|
||||
If None, create a figure with a single axes.
|
||||
useblit : bool
|
||||
If True, use blitting to speed up animation. Only available on some
|
||||
backends. If None, set to True when using Agg backend, otherwise False.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
image_viewer : ImageViewer
|
||||
Window containing image used in measurement.
|
||||
image : array
|
||||
Image used in measurement/manipulation.
|
||||
overlay : array
|
||||
Image used in measurement/manipulation.
|
||||
"""
|
||||
def __init__(self, image_viewer, useblit=None, **kwargs):
|
||||
Plugin.__init__(self, image_viewer, **kwargs)
|
||||
# Add plot for displaying intensity profile.
|
||||
self.add_plot()
|
||||
self.connect_event('draw_event', self.on_draw)
|
||||
|
||||
def on_draw(self, event):
|
||||
"""Save image background when blitting.
|
||||
|
||||
The saved image is used to "clear" the figure before redrawing artists.
|
||||
"""
|
||||
if self.useblit:
|
||||
bbox = self.image_viewer.ax.bbox
|
||||
self.img_background = self.image_viewer.canvas.copy_from_bbox(bbox)
|
||||
|
||||
def add_plot(self, height=4, width=4):
|
||||
self.canvas = PlotCanvas(self, height, width)
|
||||
self.fig = self.canvas.fig
|
||||
#TODO: Converted color is slightly different than Qt background.
|
||||
qpalette = QtGui.QPalette()
|
||||
qcolor = qpalette.color(QtGui.QPalette.Window)
|
||||
bgcolor = qcolor.toRgb().value()
|
||||
if np.isscalar(bgcolor):
|
||||
bgcolor = str(bgcolor / 255.)
|
||||
self.fig.patch.set_facecolor(bgcolor)
|
||||
self.ax = self.canvas.ax
|
||||
self.layout.addWidget(self.canvas, self.row, 0)
|
||||
|
||||
@@ -17,4 +17,4 @@ class CannyPlugin(Plugin):
|
||||
|
||||
def callback(self, *args, **kwargs):
|
||||
image = canny(*args, **kwargs)
|
||||
self._viewer.overlay = image
|
||||
self.image_viewer.overlay = image
|
||||
|
||||
@@ -53,6 +53,17 @@ class ImageViewer(QtGui.QMainWindow):
|
||||
self.canvas = ImageCanvas(self.main_widget, image)
|
||||
self.fig = self.canvas.fig
|
||||
self.ax = self.canvas.ax
|
||||
self.ax.autoscale(enable=False)
|
||||
self.image_plot = self.ax.images[0]
|
||||
self.plugins = []
|
||||
|
||||
# List of axes artists to check for removal.
|
||||
self._axes_artists = [self.ax.artists,
|
||||
self.ax.collections,
|
||||
self.ax.images,
|
||||
self.ax.lines,
|
||||
self.ax.patches,
|
||||
self.ax.texts]
|
||||
|
||||
self.layout = QtGui.QVBoxLayout(self.main_widget)
|
||||
self.layout.addWidget(self.canvas)
|
||||
@@ -61,6 +72,8 @@ class ImageViewer(QtGui.QMainWindow):
|
||||
# self.statusBar().showMessage("coordinates")
|
||||
self.original_image = image
|
||||
self.image = image
|
||||
|
||||
self.overlay_plot = None
|
||||
self._overlay = None
|
||||
|
||||
@property
|
||||
@@ -80,10 +93,10 @@ class ImageViewer(QtGui.QMainWindow):
|
||||
@overlay.setter
|
||||
def overlay(self, image):
|
||||
self._overlay = image
|
||||
if len(self.ax.images) == 1:
|
||||
self.ax.imshow(image, cmap=self.overlay_cmap)
|
||||
if self.overlay_plot is None:
|
||||
self.overlay_plot = self.ax.imshow(image, cmap=self.overlay_cmap)
|
||||
else:
|
||||
self.ax.images[1].set_array(image)
|
||||
self.overlay_plot.set_array(image)
|
||||
self.canvas.draw_idle()
|
||||
|
||||
def closeEvent(self, ce):
|
||||
@@ -93,4 +106,32 @@ class ImageViewer(QtGui.QMainWindow):
|
||||
super(ImageViewer, self).show()
|
||||
sys.exit(qApp.exec_())
|
||||
|
||||
@property
|
||||
def climits(self):
|
||||
return self.image_plot.get_clim()
|
||||
|
||||
@climits.setter
|
||||
def climits(self, limits):
|
||||
cmin, cmax = limits
|
||||
self.image_plot.set_clim(vmin=cmin, vmax=cmax)
|
||||
|
||||
def connect_event(self, event, callback):
|
||||
cid = self.canvas.mpl_connect(event, callback)
|
||||
return cid
|
||||
|
||||
def disconnect_event(self, callback_id):
|
||||
self.canvas.mpl_disconnect(callback_id)
|
||||
|
||||
def add_artist(self, artist):
|
||||
self.ax.add_artist(artist)
|
||||
|
||||
def remove_artist(self, artist):
|
||||
"""Disconnect all artists created by this widget."""
|
||||
# There's probably a smarter way to do this.
|
||||
for artist_list in self._axes_artists:
|
||||
if artist in artist_list:
|
||||
artist_list.remove(artist)
|
||||
|
||||
def redraw(self):
|
||||
self.canvas.draw_idle()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user