Fix figure canvas creation.

Avoid use of matplotlib's pyplot functions for creating figure canvases. In particular, key press events were ignored by the canvas.
This commit is contained in:
Tony S Yu
2012-12-09 20:51:18 -05:00
parent 9d653fec78
commit ef2bb54c45
3 changed files with 95 additions and 68 deletions
+5 -19
View File
@@ -5,25 +5,11 @@ try:
except ImportError:
print("Could not import PyQt4 -- skimage.viewer not available.")
try:
import matplotlib.pyplot as plt
except ImportError:
print("Could not import matplotlib -- skimage.viewer not available.")
from ..utils import MatplotlibCanvas
from ..utils import new_plot
from .base import Plugin
class PlotCanvas(MatplotlibCanvas):
"""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):
self.fig, self.ax = plt.subplots(figsize=(height, width), **kwargs)
super(PlotCanvas, self).__init__(parent, self.fig, **kwargs)
self.setMinimumHeight(150)
__all__ = ['PlotPlugin']
class PlotPlugin(Plugin):
@@ -45,8 +31,9 @@ class PlotPlugin(Plugin):
self.canvas.draw_idle()
def add_plot(self, height=4, width=4):
self.canvas = PlotCanvas(self, height, width)
self.fig = self.canvas.fig
self.fig, self.ax = new_plot(figsize=(height, width))
self.canvas = self.fig.canvas
self.canvas.setMinimumHeight(150)
#TODO: Converted color is slightly different than Qt background.
qpalette = QtGui.QPalette()
qcolor = qpalette.color(QtGui.QPalette.Window)
@@ -54,5 +41,4 @@ class PlotPlugin(Plugin):
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)
+83 -38
View File
@@ -3,8 +3,11 @@ import warnings
import numpy as np
try:
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.figure import Figure
from matplotlib import _pylab_helpers
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.backends.backend_qt4 import FigureManagerQT
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
except ImportError:
FigureCanvasQTAgg = object # hack to prevent nosetest and autodoc errors
@@ -18,7 +21,7 @@ except ImportError:
__all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage',
'LinearColormap', 'ClearColormap', 'MatplotlibCanvas']
'LinearColormap', 'ClearColormap', 'FigureCanvas', 'new_plot']
QApp = None
@@ -55,38 +58,6 @@ class RequiredAttr(object):
self.val = val
def figimage(image, scale=1, dpi=None, **kwargs):
"""Return figure and axes with figure tightly surrounding image.
Unlike pyplot.figimage, this actually plots onto an axes object, which
fills the figure. Plotting the image onto an axes allows for subsequent
overlays of axes artists.
Parameters
----------
image : array
image to plot
scale : float
If scale is 1, the figure and axes have the same dimension as the
image. Smaller values of `scale` will shrink the figure.
dpi : int
Dots per inch for figure. If None, use the default rcParam.
"""
dpi = dpi if dpi is not None else plt.rcParams['figure.dpi']
kwargs.setdefault('interpolation', 'nearest')
kwargs.setdefault('cmap', 'gray')
h, w, d = np.atleast_3d(image).shape
figsize = np.array((w, h), dtype=float) / dpi * scale
fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
fig.subplots_adjust(left=0, bottom=0, right=1, top=1)
ax.set_axis_off()
ax.imshow(image, **kwargs)
return fig, ax
class LinearColormap(LinearSegmentedColormap):
"""LinearSegmentedColormap in which color varies smoothly.
@@ -124,14 +95,88 @@ class ClearColormap(LinearColormap):
LinearColormap.__init__(self, name, cg_speq)
class MatplotlibCanvas(FigureCanvasQTAgg):
class FigureCanvas(FigureCanvasQTAgg):
"""Canvas for displaying images."""
def __init__(self, parent, figure, **kwargs):
def __init__(self, figure, **kwargs):
self.fig = figure
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)
#TODO: Consider overriding Matplotlib key-event handling
# def keyPressEvent(self, event):
# # Override key events defined by Matplotlib
# event.ignore()
# def keyReleaseEvent(self, event):
# # Override key events defined by Matplotlib
# event.ignore()
def new_canvas(*args, **kwargs):
"""Return a new figure canvas."""
allnums = _pylab_helpers.Gcf.figs.keys()
num = max(allnums) + 1 if allnums else 1
FigureClass = kwargs.pop('FigureClass', Figure)
figure = FigureClass(*args, **kwargs)
canvas = FigureCanvas(figure)
fig_manager = FigureManagerQT(canvas, num)
return fig_manager.canvas
def new_plot(parent=None, subplot_kw=None, **fig_kw):
"""Return new figure and axes.
Parameters
----------
parent : QtWidget
Qt widget that displays the plot objects. If None, you must manually
call ``canvas.setParent`` and pass the parent widget.
subplot_kw : dict
Keyword arguments passed ``matplotlib.figure.Figure.add_subplot``.
fig_kw : dict
Keyword arguments passed ``matplotlib.figure.Figure``.
"""
if subplot_kw is None:
subplot_kw = {}
canvas = new_canvas(**fig_kw)
canvas.setParent(parent)
fig = canvas.figure
ax = fig.add_subplot(1, 1, 1, **subplot_kw)
return fig, ax
def figimage(image, scale=1, dpi=None, **kwargs):
"""Return figure and axes with figure tightly surrounding image.
Unlike pyplot.figimage, this actually plots onto an axes object, which
fills the figure. Plotting the image onto an axes allows for subsequent
overlays of axes artists.
Parameters
----------
image : array
image to plot
scale : float
If scale is 1, the figure and axes have the same dimension as the
image. Smaller values of `scale` will shrink the figure.
dpi : int
Dots per inch for figure. If None, use the default rcParam.
"""
dpi = dpi if dpi is not None else mpl.rcParams['figure.dpi']
kwargs.setdefault('interpolation', 'nearest')
kwargs.setdefault('cmap', 'gray')
h, w, d = np.atleast_3d(image).shape
figsize = np.array((w, h), dtype=float) / dpi * scale
fig, ax = new_plot(figsize=figsize, dpi=dpi)
fig.subplots_adjust(left=0, bottom=0, right=1, top=1)
ax.set_axis_off()
ax.imshow(image, **kwargs)
return fig, ax
+7 -11
View File
@@ -16,13 +16,6 @@ from ..widgets import Slider
__all__ = ['ImageViewer', 'CollectionViewer']
class ImageCanvas(utils.MatplotlibCanvas):
"""Canvas for displaying images."""
def __init__(self, parent, image, **kwargs):
self.fig, self.ax = utils.figimage(image, **kwargs)
super(ImageCanvas, self).__init__(parent, self.fig, **kwargs)
class ImageViewer(QMainWindow):
"""Viewer for displaying images.
@@ -72,9 +65,10 @@ class ImageViewer(QMainWindow):
self.main_widget = QtGui.QWidget()
self.setCentralWidget(self.main_widget)
self.canvas = ImageCanvas(self.main_widget, image)
self.fig = self.canvas.fig
self.ax = self.canvas.ax
self.fig, self.ax = utils.figimage(image)
self.canvas = self.fig.canvas
self.canvas.setParent(self)
self.ax.autoscale(enable=False)
self._image_plot = self.ax.images[0]
@@ -274,6 +268,8 @@ class CollectionViewer(ImageViewer):
if 48 <= key < 58:
index = 0.1 * int(key - 48) * self.num_images
self.update_index('', index)
event.accept()
event.accept()
else:
event.ignore()
else:
event.ignore()