Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

View file

@ -0,0 +1,9 @@
from .base import Plugin
from .canny import CannyPlugin
from .color_histogram import ColorHistogram
from .crop import Crop
from .labelplugin import LabelPainter
from .lineprofile import LineProfile
from .measure import Measure
from .overlayplugin import OverlayPlugin
from .plotplugin import PlotPlugin

View file

@ -0,0 +1,261 @@
"""
Base class for Plugins that interact with ImageViewer.
"""
from warnings import warn
import numpy as np
from ..qt import QtWidgets, QtCore, Signal
from ..utils import RequiredAttr, init_qtapp
class Plugin(QtWidgets.QDialog):
"""Base class for plugins that interact with an ImageViewer.
A plugin connects an image filter (or another function) to an image viewer.
Note that a Plugin is initialized *without* an image viewer and attached in
a later step. See example below for details.
Parameters
----------
image_viewer : ImageViewer
Window containing image used in measurement/manipulation.
image_filter : function
Function that gets called to update image in image viewer. This value
can be `None` if, for example, you have a plugin that extracts
information from an image and doesn't manipulate it. Alternatively,
this function can be defined as a method in a Plugin subclass.
height, width : int
Size of plugin window in pixels. Note that Qt will automatically resize
a window to fit components. So if you're adding rows of components, you
can leave `height = 0` and just let Qt determine the final height.
useblit : bool
If True, use blitting to speed up animation. Only available on some
Matplotlib backends. If None, set to True when using Agg backend.
This only has an effect if you draw on top of an image viewer.
Attributes
----------
image_viewer : ImageViewer
Window containing image used in measurement.
name : str
Name of plugin. This is displayed as the window title.
artist : list
List of Matplotlib artists and canvastools. Any artists created by the
plugin should be added to this list so that it gets cleaned up on
close.
Examples
--------
>>> from skimage.viewer import ImageViewer
>>> from skimage.viewer.widgets import Slider
>>> from skimage import data
>>>
>>> plugin = Plugin(image_filter=lambda img,
... threshold: img > threshold) # doctest: +SKIP
>>> plugin += Slider('threshold', 0, 255) # doctest: +SKIP
>>>
>>> image = data.coins()
>>> viewer = ImageViewer(image) # doctest: +SKIP
>>> viewer += plugin # doctest: +SKIP
>>> thresholded = viewer.show()[0][0] # doctest: +SKIP
The plugin will automatically delegate parameters to `image_filter` based
on its parameter type, i.e., `ptype` (widgets for required arguments must
be added in the order they appear in the function). The image attached
to the viewer is **automatically passed as the first argument** to the
filter function.
#TODO: Add flag so image is not passed to filter function by default.
`ptype = 'kwarg'` is the default for most widgets so it's unnecessary here.
"""
name = 'Plugin'
image_viewer = RequiredAttr("%s is not attached to ImageViewer" % name)
# Signals used when viewers are linked to the Plugin output.
image_changed = Signal(np.ndarray)
_started = Signal(int)
def __init__(self, image_filter=None, height=0, width=400, useblit=True,
dock='bottom'):
init_qtapp()
super(Plugin, self).__init__()
self.dock = dock
self.image_viewer = None
# 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 = QtWidgets.QGridLayout(self)
self.resize(width, height)
self.row = 0
self.arguments = []
self.keyword_arguments = {}
self.useblit = useblit
self.cids = []
self.artists = []
def attach(self, image_viewer):
"""Attach the plugin to an ImageViewer.
Note that the ImageViewer will automatically call this method when the
plugin is added to the ImageViewer. For example::
viewer += Plugin(...)
Also note that `attach` automatically calls the filter function so that
the image matches the filtered value specified by attached widgets.
"""
self.setParent(image_viewer)
self.setWindowFlags(QtCore.Qt.Dialog)
self.image_viewer = image_viewer
self.image_viewer.plugins.append(self)
#TODO: Always passing image as first argument may be bad assumption.
self.arguments = [self.image_viewer.original_image]
# Call filter so that filtered image matches widget values
self.filter_image()
def add_widget(self, widget):
"""Add widget to plugin.
Alternatively, Plugin's `__add__` method is overloaded to add widgets::
plugin += Widget(...)
Widgets can adjust required or optional arguments of filter function or
parameters for the plugin. This is specified by the Widget's `ptype`.
"""
if widget.ptype == 'kwarg':
name = widget.name.replace(' ', '_')
self.keyword_arguments[name] = widget
widget.callback = self.filter_image
elif widget.ptype == 'arg':
self.arguments.append(widget)
widget.callback = self.filter_image
elif widget.ptype == 'plugin':
widget.callback = self.update_plugin
widget.plugin = self
self.layout.addWidget(widget, self.row, 0)
self.row += 1
def __add__(self, widget):
self.add_widget(widget)
return self
def filter_image(self, *widget_arg):
"""Call `image_filter` with widget args and kwargs
Note: `display_filtered_image` is automatically called.
"""
# `widget_arg` is passed by the active widget but is unused since all
# filter arguments are pulled directly from attached the widgets.
if self.image_filter is None:
return
arguments = [self._get_value(a) for a in self.arguments]
kwargs = {name: self._get_value(a)
for name, a in self.keyword_arguments.items()}
filtered = self.image_filter(*arguments, **kwargs)
self.display_filtered_image(filtered)
self.image_changed.emit(filtered)
def _get_value(self, param):
# If param is a widget, return its `val` attribute.
return param if not hasattr(param, 'val') else param.val
def _update_original_image(self, image):
"""Update the original image argument passed to the filter function.
This method is called by the viewer when the original image is updated.
"""
self.arguments[0] = image
self._on_new_image(image)
self.filter_image()
def _on_new_image(self, image):
"""Override this method to update your plugin for new images."""
pass
@property
def filtered_image(self):
"""Return filtered image."""
return self.image_viewer.image
def display_filtered_image(self, image):
"""Display the filtered image on image viewer.
If you don't want to simply replace the displayed image with the
filtered image (e.g., you want to display a transparent overlay),
you can override this method.
"""
self.image_viewer.image = image
def update_plugin(self, name, value):
"""Update keyword parameters of the plugin itself.
These parameters will typically be implemented as class properties so
that they update the image or some other component.
"""
setattr(self, name, value)
def show(self, main_window=True):
"""Show plugin."""
super(Plugin, self).show()
self.activateWindow()
self.raise_()
# Emit signal with x-hint so new windows can be displayed w/o overlap.
size = self.frameGeometry()
x_hint = size.x() + size.width()
self._started.emit(x_hint)
def closeEvent(self, event):
"""On close disconnect all artists and events from ImageViewer.
Note that artists must be appended to `self.artists`.
"""
self.clean_up()
self.close()
def clean_up(self):
self.remove_image_artists()
if self in self.image_viewer.plugins:
self.image_viewer.plugins.remove(self)
self.image_viewer.reset_image()
self.image_viewer.redraw()
def remove_image_artists(self):
"""Remove artists that are connected to the image viewer."""
for a in self.artists:
a.remove()
def output(self):
"""Return the plugin's representation and data.
Returns
-------
image : array, same shape as ``self.image_viewer.image``, or None
The filtered image.
data : None
Any data associated with the plugin.
Notes
-----
Derived classes should override this method to return a tuple
containing an *overlay* of the same shape of the image, and a
*data* object. Either of these is optional: return ``None`` if
you don't want to return a value.
"""
return (self.image_viewer.image, None)

View file

@ -0,0 +1,30 @@
import numpy as np
import skimage
from ...feature import canny
from .overlayplugin import OverlayPlugin
from ..widgets import Slider, ComboBox
class CannyPlugin(OverlayPlugin):
"""Canny filter plugin to show edges of an image."""
name = 'Canny Filter'
def __init__(self, *args, **kwargs):
super(CannyPlugin, self).__init__(image_filter=canny, **kwargs)
def attach(self, image_viewer):
image = image_viewer.image
imin, imax = skimage.dtype_limits(image, clip_negative=False)
itype = 'float' if np.issubdtype(image.dtype, np.floating) else 'int'
self.add_widget(Slider('sigma', 0, 5, update_on='release'))
self.add_widget(Slider('low threshold', imin, imax, value_type=itype,
update_on='release'))
self.add_widget(Slider('high threshold', imin, imax, value_type=itype,
update_on='release'))
self.add_widget(ComboBox('color', self.color_names, ptype='plugin'))
# Call parent method at end b/c it calls `filter_image`, which needs
# the values specified by the widgets. Alternatively, move call to
# parent method to beginning and add a call to `self.filter_image()`
super(CannyPlugin,self).attach(image_viewer)

View file

@ -0,0 +1,93 @@
import numpy as np
import matplotlib.pyplot as plt
from ... import color, exposure
from .plotplugin import PlotPlugin
from ..canvastools import RectangleTool
class ColorHistogram(PlotPlugin):
name = 'Color Histogram'
def __init__(self, max_pct=0.99, **kwargs):
super(ColorHistogram, self).__init__(height=400, **kwargs)
self.max_pct = max_pct
print(self.help())
def attach(self, image_viewer):
super(ColorHistogram, self).attach(image_viewer)
self.rect_tool = RectangleTool(self,
on_release=self.ab_selected)
self._on_new_image(image_viewer.image)
def _on_new_image(self, image):
self.lab_image = color.rgb2lab(image)
# Calculate color histogram in the Lab colorspace:
L, a, b = self.lab_image.T
left, right = -100, 100
ab_extents = [left, right, right, left]
self.mask = np.ones(L.shape, bool)
bins = np.arange(left, right)
hist, x_edges, y_edges = np.histogram2d(a.flatten(), b.flatten(),
bins, normed=True)
self.data = {'bins': bins, 'hist': hist, 'edges': (x_edges, y_edges),
'extents': (left, right, left, right)}
# Clip bin heights that dominate a-b histogram
max_val = pct_total_area(hist, percentile=self.max_pct)
hist = exposure.rescale_intensity(hist, in_range=(0, max_val))
self.ax.imshow(hist, extent=ab_extents, cmap=plt.cm.gray)
self.ax.set_title('Color Histogram')
self.ax.set_xlabel('b')
self.ax.set_ylabel('a')
def help(self):
helpstr = ("Color Histogram tool:",
"Select region of a-b colorspace to highlight on image.")
return '\n'.join(helpstr)
def ab_selected(self, extents):
x0, x1, y0, y1 = extents
self.data['extents'] = extents
lab_masked = self.lab_image.copy()
L, a, b = lab_masked.T
self.mask = ((a > y0) & (a < y1)) & ((b > x0) & (b < x1))
lab_masked[..., 1:][~self.mask.T] = 0
self.image_viewer.image = color.lab2rgb(lab_masked)
def output(self):
"""Return the image mask and the histogram data.
Returns
-------
mask : array of bool, same shape as image
The selected pixels.
data : dict
The data describing the histogram and the selected region.
The dictionary contains:
- 'bins' : array of float
The bin boundaries for both `a` and `b` channels.
- 'hist' : 2D array of float
The normalized histogram.
- 'edges' : tuple of array of float
The bin edges along each dimension
- 'extents' : tuple of float
The left and right and top and bottom of the selected region.
"""
return (self.mask, self.data)
def pct_total_area(image, percentile=0.80):
"""Return threshold value based on percentage of total area.
The specified percent of pixels less than the given intensity threshold.
"""
idx = int((image.size - 1) * percentile)
sorted_pixels = np.sort(image.flat)
return sorted_pixels[idx]

View file

@ -0,0 +1,45 @@
from .base import Plugin
from ..canvastools import RectangleTool
from ...viewer.widgets import SaveButtons, Button
__all__ = ['Crop']
class Crop(Plugin):
name = 'Crop'
def __init__(self, maxdist=10, **kwargs):
super(Crop, self).__init__(**kwargs)
self.maxdist = maxdist
self.add_widget(SaveButtons())
print(self.help())
def attach(self, image_viewer):
super(Crop, self).attach(image_viewer)
self.rect_tool = RectangleTool(image_viewer,
maxdist=self.maxdist,
on_enter=self.crop)
self.artists.append(self.rect_tool)
self.reset_button = Button('Reset', self.reset)
self.add_widget(self.reset_button)
def help(self):
helpstr = ("Crop tool",
"Select rectangular region and press enter to crop.")
return '\n'.join(helpstr)
def crop(self, extents):
xmin, xmax, ymin, ymax = extents
if xmin == xmax or ymin == ymax:
return
image = self.image_viewer.image[ymin:ymax+1, xmin:xmax+1]
self.image_viewer.image = image
self.image_viewer.ax.relim()
def reset(self):
self.rect_tool.extents = -10, -10, -10, -10
self.image_viewer.image = self.image_viewer.original_image
self.image_viewer.ax.relim()

View file

@ -0,0 +1,67 @@
import numpy as np
from .base import Plugin
from ..widgets import ComboBox, Slider
from ..canvastools import PaintTool
__all__ = ['LabelPainter']
rad2deg = 180 / np.pi
class LabelPainter(Plugin):
name = 'LabelPainter'
def __init__(self, max_radius=20, **kwargs):
super(LabelPainter, self).__init__(**kwargs)
# These widgets adjust plugin properties instead of an image filter.
self._radius_widget = Slider('radius', low=1, high=max_radius,
value=5, value_type='int', ptype='plugin')
labels = [str(i) for i in range(6)]
labels[0] = 'Erase'
self._label_widget = ComboBox('label', labels, ptype='plugin')
self.add_widget(self._radius_widget)
self.add_widget(self._label_widget)
print(self.help())
def help(self):
helpstr = ("Label painter",
"Hold left-mouse button and paint on canvas.")
return '\n'.join(helpstr)
def attach(self, image_viewer):
super(LabelPainter, self).attach(image_viewer)
image = image_viewer.original_image
self.paint_tool = PaintTool(image_viewer, image.shape,
on_enter=self.on_enter)
self.paint_tool.radius = self.radius
self.paint_tool.label = self._label_widget.index = 1
self.artists.append(self.paint_tool)
def _on_new_image(self, image):
"""Update plugin for new images."""
self.paint_tool.shape = image.shape
def on_enter(self, overlay):
pass
@property
def radius(self):
return self._radius_widget.val
@radius.setter
def radius(self, val):
self.paint_tool.radius = val
@property
def label(self):
return self._label_widget.val
@label.setter
def label(self, val):
self.paint_tool.label = val

View file

@ -0,0 +1,165 @@
import numpy as np
from ...util.dtype import dtype_range
from ... import draw, measure
from .plotplugin import PlotPlugin
from ..canvastools import ThickLineTool
__all__ = ['LineProfile']
class LineProfile(PlotPlugin):
"""Plugin to compute interpolated intensity under a scan line on an image.
See PlotPlugin and Plugin classes for additional details.
Parameters
----------
maxdist : float
Maximum pixel distance allowed when selecting end point of scan line.
limits : tuple or {None, 'image', 'dtype'}
(minimum, maximum) intensity limits for plotted profile. The following
special values are defined:
None : rescale based on min/max intensity along selected scan line.
'image' : fixed scale based on min/max intensity in image.
'dtype' : fixed scale based on min/max intensity of image dtype.
"""
name = 'Line Profile'
def __init__(self, maxdist=10, epsilon='deprecated',
limits='image', **kwargs):
super(LineProfile, self).__init__(**kwargs)
self.maxdist = maxdist
self._limit_type = limits
print(self.help())
def attach(self, image_viewer):
super(LineProfile, self).attach(image_viewer)
image = image_viewer.original_image
if self._limit_type == 'image':
self.limits = (np.min(image), np.max(image))
elif self._limit_type == 'dtype':
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:
raise ValueError("Unrecognized `limits`: %s" % self._limit_type)
if not self._limit_type is None:
self.ax.set_ylim(self.limits)
h, w = image.shape[0:2]
x = [w / 3, 2 * w / 3]
y = [h / 2] * 2
self.line_tool = ThickLineTool(self.image_viewer,
maxdist=self.maxdist,
on_move=self.line_changed,
on_change=self.line_changed)
self.line_tool.end_points = np.transpose([x, y])
scan_data = measure.profile_line(image,
*self.line_tool.end_points[:, ::-1])
self.scan_data = scan_data
if scan_data.ndim == 1:
scan_data = scan_data[:, np.newaxis]
self.reset_axes(scan_data)
self._autoscale_view()
def help(self):
helpstr = ("Line profile tool",
"+ and - keys or mouse scroll changes width of scan line.",
"Select and drag ends of the scan line to adjust it.")
return '\n'.join(helpstr)
def get_profiles(self):
"""Return intensity profile of the selected line.
Returns
-------
end_points: (2, 2) array
The positions ((x1, y1), (x2, y2)) of the line ends.
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
def _autoscale_view(self):
if self.limits is None:
self.ax.autoscale_view(tight=True)
else:
self.ax.autoscale_view(scaley=False, tight=True)
def line_changed(self, end_points):
x, y = np.transpose(end_points)
self.line_tool.end_points = end_points
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:
scan = scan[:, np.newaxis]
if scan.shape[1] != len(self.profile):
self.reset_axes(scan)
for i in range(len(scan[0])):
self.profile[i].set_xdata(np.arange(scan.shape[0]))
self.profile[i].set_ydata(scan[:, i])
def reset_axes(self, scan_data):
# Clear lines out
for line in self.ax.lines:
self.ax.lines = []
if scan_data.shape[1] == 1:
self.profile = self.ax.plot(scan_data, 'k-')
else:
self.profile = self.ax.plot(scan_data[:, 0], 'r-',
scan_data[:, 1], 'g-',
scan_data[:, 2], 'b-')
def output(self):
"""Return the drawn line and the resulting scan.
Returns
-------
line_image : (M, N) uint8 array, same shape as image
An array of 0s with the scanned line set to 255.
If the linewidth of the line tool is greater than 1,
sets the values within the profiled polygon to 128.
scan : (P,) or (P, 3) array of int or float
The line scan values across the image.
"""
end_points = self.line_tool.end_points
line_image = np.zeros(self.image_viewer.image.shape[:2],
np.uint8)
width = self.line_tool.linewidth
if width > 1:
rp, cp = measure.profile._line_profile_coordinates(
*end_points[:, ::-1], linewidth=width)
# the points are aliased, so create a polygon using the corners
yp = np.rint(rp[[0, 0, -1, -1],[0, -1, -1, 0]]).astype(int)
xp = np.rint(cp[[0, 0, -1, -1],[0, -1, -1, 0]]).astype(int)
rp, cp = draw.polygon(yp, xp, line_image.shape)
line_image[rp, cp] = 128
(x1, y1), (x2, y2) = end_points.astype(int)
rr, cc = draw.line(y1, x1, y2, x2)
line_image[rr, cc] = 255
return line_image, self.scan_data

View file

@ -0,0 +1,49 @@
import numpy as np
from .base import Plugin
from ..widgets import Text
from ..canvastools import LineTool
__all__ = ['Measure']
rad2deg = 180 / np.pi
class Measure(Plugin):
name = 'Measure'
def __init__(self, maxdist=10, **kwargs):
super(Measure, self).__init__(**kwargs)
self.maxdist = maxdist
self._length = Text('Length:')
self._angle = Text('Angle:')
self.add_widget(self._length)
self.add_widget(self._angle)
print(self.help())
def attach(self, image_viewer):
super(Measure, self).attach(image_viewer)
image = image_viewer.original_image
h, w = image.shape
self.line_tool = LineTool(self.image_viewer,
maxdist=self.maxdist,
on_move=self.line_changed)
self.artists.append(self.line_tool)
def help(self):
helpstr = ("Measure tool",
"Select line to measure distance and angle.")
return '\n'.join(helpstr)
def line_changed(self, end_points):
x, y = np.transpose(end_points)
dx = np.diff(x)[0]
dy = np.diff(y)[0]
self._length.text = '%.1f' % np.hypot(dx, dy)
self._angle.text = '%.1f°' % (180 - np.arctan2(dy, dx) * rad2deg)

View file

@ -0,0 +1,115 @@
from ...util.dtype import dtype_range
from .base import Plugin
from ..utils import ClearColormap, update_axes_image
from ..._shared.version_requirements import is_installed
__all__ = ['OverlayPlugin']
class OverlayPlugin(Plugin):
"""Plugin for ImageViewer that displays an overlay on top of main image.
The base Plugin class displays the filtered image directly on the viewer.
OverlayPlugin will instead overlay an image with a transparent colormap.
See base Plugin class for additional details.
Attributes
----------
overlay : array
Overlay displayed on top of image. This overlay defaults to a color map
with alpha values varying linearly from 0 to 1.
color : int
Color of overlay.
"""
colors = {'red': (1, 0, 0),
'yellow': (1, 1, 0),
'green': (0, 1, 0),
'cyan': (0, 1, 1)}
def __init__(self, **kwargs):
super(OverlayPlugin, self).__init__(**kwargs)
self._overlay_plot = None
self._overlay = None
self.cmap = None
self.color_names = sorted(list(self.colors.keys()))
def attach(self, image_viewer):
super(OverlayPlugin, self).attach(image_viewer)
#TODO: `color` doesn't update GUI widget when set manually.
self.color = 0
@property
def overlay(self):
return self._overlay
@overlay.setter
def overlay(self, image):
self._overlay = image
ax = self.image_viewer.ax
if image is None:
ax.images.remove(self._overlay_plot)
self._overlay_plot = None
elif self._overlay_plot is None:
vmin, vmax = dtype_range[image.dtype.type]
self._overlay_plot = ax.imshow(image, cmap=self.cmap,
vmin=vmin, vmax=vmax)
else:
update_axes_image(self._overlay_plot, image)
if self.image_viewer.useblit:
self.image_viewer._blit_manager.background = None
self.image_viewer.redraw()
@property
def color(self):
return self._color
@color.setter
def color(self, index):
# Update colormap whenever color is changed.
if isinstance(index, str) and \
index not in self.color_names:
raise ValueError("%s not defined in OverlayPlugin.colors" % index)
else:
name = self.color_names[index]
self._color = name
rgb = self.colors[name]
self.cmap = ClearColormap(rgb)
if self._overlay_plot is not None:
self._overlay_plot.set_cmap(self.cmap)
self.image_viewer.redraw()
@property
def filtered_image(self):
"""Return filtered image.
This "filtered image" is used when saving from the plugin.
"""
return self.overlay
def display_filtered_image(self, image):
"""Display filtered image as an overlay on top of image in viewer."""
self.overlay = image
def closeEvent(self, event):
# clear overlay from ImageViewer on close
self.overlay = None
super(OverlayPlugin, self).closeEvent(event)
def output(self):
"""Return the overlaid image.
Returns
-------
overlay : array, same shape as image
The overlay currently displayed.
data : None
"""
return (self.overlay, None)

View file

@ -0,0 +1,74 @@
import numpy as np
from ..qt import QtGui
from ..utils import new_plot
from ..utils.canvas import BlitManager, EventManager
from .base import Plugin
__all__ = ['PlotPlugin']
class PlotPlugin(Plugin):
"""Plugin for ImageViewer that contains a plot canvas.
Base class for plugins that contain a Matplotlib plot canvas, which can,
for example, display an image histogram.
See base Plugin class for additional details.
"""
def __init__(self, image_filter=None, height=150, width=400, **kwargs):
super(PlotPlugin, self).__init__(image_filter=image_filter,
height=height, width=width, **kwargs)
self._height = height
self._width = width
self._blit_manager = None
self._tools = []
self._event_manager = None
def attach(self, image_viewer):
super(PlotPlugin, self).attach(image_viewer)
# Add plot for displaying intensity profile.
self.add_plot()
if image_viewer.useblit:
self._blit_manager = BlitManager(self.ax)
self._event_manager = EventManager(self.ax)
def redraw(self):
"""Redraw plot."""
self.canvas.draw_idle()
def add_plot(self):
self.fig, self.ax = new_plot()
self.fig.set_figwidth(self._width / float(self.fig.dpi))
self.fig.set_figheight(self._height / float(self.fig.dpi))
self.canvas = self.fig.canvas
#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.layout.addWidget(self.canvas, self.row, 0)
def _update_original_image(self, image):
super(PlotPlugin, self)._update_original_image(image)
self.redraw()
def add_tool(self, tool):
if self._blit_manager:
self._blit_manager.add_artists(tool.artists)
self._tools.append(tool)
self._event_manager.attach(tool)
def remove_tool(self, tool):
if tool not in self._tools:
return
if self._blit_manager:
self._blit_manager.remove_artists(tool.artists)
self._tools.remove(tool)
self._event_manager.detach(tool)