ENH: Allow Plugin.add_widget to hook into Plugin attributes.

The `ptype` parameter of widget can now be set to 'plugin'. When this is the case, the plugin will set a plugin attribute whenever the widget is updated.

As an example, this commit adds a ComboBox widget which is hooked into the overlay color of the OverlayPlugin.
This commit is contained in:
Tony S Yu
2012-07-22 13:24:41 -04:00
parent 06449581bd
commit 86b428952d
5 changed files with 99 additions and 13 deletions
+6 -1
View File
@@ -1,7 +1,6 @@
from PyQt4 import QtGui
import matplotlib as mpl
from ..widgets import Slider
class Plugin(QtGui.QDialog):
@@ -43,6 +42,7 @@ class Plugin(QtGui.QDialog):
if image_filter is not None:
self.image_filter = image_filter
#TODO: Always passing image as first argument may be bad assumption.
self.arguments = [image_viewer.original_image]
self.keyword_arguments= {}
@@ -86,9 +86,14 @@ class Plugin(QtGui.QDialog):
elif widget.ptype == 'arg':
self.arguments.append(widget)
widget.callback = self.filter_image
elif widget.ptype == 'plugin':
widget.callback = self.update_plugin
self.layout.addWidget(widget, self.row, 0)
self.row += 1
def update_plugin(self, name, value):
setattr(self, name, value)
def closeEvent(self, event):
"""Disconnect all artists and events from ImageViewer.
+2 -1
View File
@@ -1,7 +1,7 @@
from skimage.filter import canny
from .overlayplugin import OverlayPlugin
from ..widgets import Slider
from ..widgets import Slider, ComboBox
class CannyPlugin(OverlayPlugin):
@@ -16,6 +16,7 @@ class CannyPlugin(OverlayPlugin):
self.add_widget(Slider('sigma', 0, 5, update_on='release'))
self.add_widget(Slider('low threshold', 0, 255, update_on='release'))
self.add_widget(Slider('high threshold', 0, 255, update_on='release'))
self.add_widget(ComboBox('color', self.color_names, ptype='plugin'))
# Update image overlay to default slider values.
self.filter_image()
+26 -4
View File
@@ -1,6 +1,5 @@
from ..utils import clear_red
from .base import Plugin
from ..utils import ClearColormap
class OverlayPlugin(Plugin):
@@ -12,12 +11,19 @@ class OverlayPlugin(Plugin):
Overlay displayed on top of image. This overlay defaults to a color map
with alpha values varying linearly from 0 to 1.
"""
colors = {'red': (1, 0, 0),
'yellow': (1, 1, 0),
'green': (0, 1, 0),
'cyan': (0, 1, 1)}
def __init__(self, image_viewer, **kwargs):
Plugin.__init__(self, image_viewer, **kwargs)
self.overlay_cmap = clear_red
self._overlay_plot = None
self._overlay = None
self.cmap = None
self.color_names = self.colors.keys()
#TODO: `color` doesn't update GUI widget when set manually.
self.color = 0
@property
def overlay(self):
@@ -31,7 +37,7 @@ class OverlayPlugin(Plugin):
ax.images.remove(self._overlay_plot)
self._overlay_plot = None
elif self._overlay_plot is None:
self._overlay_plot = ax.imshow(image, cmap=self.overlay_cmap)
self._overlay_plot = ax.imshow(image, cmap=self.cmap)
else:
self._overlay_plot.set_array(image)
self.image_viewer.redraw()
@@ -39,3 +45,19 @@ class OverlayPlugin(Plugin):
def closeEvent(self, event):
self.overlay = None
super(OverlayPlugin, self).closeEvent(event)
@property
def color(self):
return self._color
@color.setter
def color(self, index):
# Update colormap whenever color is changed.
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()
+2 -5
View File
@@ -3,7 +3,7 @@ import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
__all__ = ['figimage', 'LinearColormap', 'ClearColormap', 'clear_red']
__all__ = ['figimage', 'LinearColormap', 'ClearColormap']
def figimage(image, scale=1, dpi=None, **kwargs):
@@ -65,13 +65,10 @@ class LinearColormap(LinearSegmentedColormap):
class ClearColormap(LinearColormap):
"""Color map that varies linearly from alpha = 0 to 1
"""
def __init__(self, name, rgb):
def __init__(self, rgb, name='clear_color'):
r, g, b = rgb
cg_speq = {'blue': [(0.0, b), (1.0, b)],
'green': [(0.0, g), (1.0, g)],
'red': [(0.0, r), (1.0, r)],
'alpha': [(0.0, 0.0), (1.0, 1.0)]}
LinearColormap.__init__(self, name, cg_speq)
clear_red = ClearColormap('clear_red', (0.7, 0, 0))
+63 -2
View File
@@ -1,5 +1,21 @@
"""
Widgets for interacting with ImageViewer.
These widgets should be added to a Plugin subclass using its `add_widget`
method. The Plugin will delegate action based on the widget's parameter type
specified by its `ptype` attribute, which can be:
'arg' : positional argument passed to Plugin's `filter_image` method.
'kwarg' : keyword argument passed to Plugin's `filter_image` method.
'plugin' : attribute of Plugin. You'll probably need to make the attribute
a class property that updates the display.
"""
from PyQt4 import QtGui
from PyQt4 import QtCore
from skimage.io._plugins.q_color_mixer import IntelligentSlider
class Slider(IntelligentSlider):
"""Slider widget.
@@ -12,11 +28,56 @@ class Slider(IntelligentSlider):
name of the slider.
low, high : float
Range of slider values.
ptype : {'arg' | 'kwarg' | ...}
Parameter
ptype : {'arg' | 'kwarg' | 'plugin'}
Parameter type.
"""
def __init__(self, name, low, high, ptype='kwarg', callback=None, **kwargs):
self.ptype = ptype
kwargs.setdefault('orientation', 'horizontal')
scale = (high - low) / 1000.0
super(Slider, self).__init__(name, scale, low, callback, **kwargs)
class ComboBox(QtGui.QWidget):
"""ComboBox widget for selecting among a list of choices.
Parameters
----------
name : str
Name of slider parameter. If this parameter is passed as a keyword
argument, it must match the name of that keyword argument (spaces are
replaced with underscores). In addition, this name is displayed as the
name of the slider.
items: list
Allowed parameter values.
ptype : {'arg' | 'kwarg' | 'plugin'}
Parameter type.
"""
def __init__(self, name, items, ptype='kwarg', callback=None):
super(ComboBox, self).__init__()
self.ptype = ptype
self.callback = callback
self.name = name
self.name_label = QtGui.QLabel()
self.name_label.setText(self.name)
self.name_label.setAlignment(QtCore.Qt.AlignLeft)
self._combo_box = QtGui.QComboBox()
self._combo_box.addItems(items)
self.layout = QtGui.QHBoxLayout(self)
self.layout.addWidget(self.name_label)
self.layout.addWidget(self._combo_box, alignment=QtCore.Qt.AlignLeft)
self._combo_box.currentIndexChanged.connect(self._value_changed)
# self.connect(self._combo_box,
# SIGNAL("currentIndexChanged(int)"), self.updateUi)
@property
def val(self):
return self._combo_box.value()
def _value_changed(self, value):
self.callback(self.name, value)