Files
scikit-image/skimage/viewer/canvastools/recttool.py
T
blink1073 b6dcf3c336 Update Travis build to use Anaconda.
Update Travis build to use Anaconda

Travis updates and fixes

More travis fixes

Another travis attempt

Revert changes

Use PIL and Pillow

Refactor travis into 4 different builds

Fix activation error

Remove explicit mpl in build_versions.py

Make matplotlib an explicit requirement

Rearrange travis

Make pillow a hard requirement

Try again to make Pillow optional

Fix bash syntax error

Fix bash syntax error

Bump required cython version

More rearrangments

Remove mpl from build_versions, rearrange travis

Fix version check

Make matplotlib explicit again

Conda install into test env

Check for proper install

Allow tests to skip if networkx is not available

Allow tests to skip if networkx is not available

Try swapping pillow for matplotlib

Allow tests to pass when matplotlib is not present

Remove matplotlib from build_versions

Print PIL version

Get pillow from PIP

Allow tests to skip if matplotlib is not present.

Allow tests to skip if networkx is not present.

travis fix

Remove unused mpl import that caused test error

Use nose-cov and do not run doctests without optional libs

Bump required numpy version and fix nose calls

Make overlay test repeatable

bump numpy version again

Move low-end numpy to python 2.7

Play with minimum versions

Add version requirements and use functions

Add version requirements and use functions

Allow require to skip a test

More implementation of require decorator

Update require decorator and clean up tests

Only use requires decorator when needed

Fix python3 error in version_requirements

Fix build errors

Fix handling of require with tests

More fixes for require handler

Use latest miniconda

Fix more build errors

Fix another dict comprehension and travis file.

Fix missing imports

Fix dictionary again

Fix import warning

Fix last failing test on 2.6

Skip doc examples on python2.6

Do not run doctests on python2.6

Fix typo in travis.yml

Make numpy-1.6 compatibility changes

Use numpy-1.6 in travis python2.6

Add tests for version requirements

Fix line noise in PR

Add additional io plugins

Fix simpleitk test.

Fix python 3 error in freeimage_plugin.

Install imread in Travis.

Put matplotlib settings in XDG recommended directory

Fix formatting in travis yml

Fix formatting in travis yml

Make sure to close PIL file atexit

Fix name of apt package xcftools

Fix pil fp closing

Fix matplotlibrc creation

Only download SimpleITK on py2x, run coverage on py27

Fix travis yml syntax error

Run coveralls on py2.7

Install SimpleITK on py3.3 and run coverage on py3.3

Make simpleitk install quiet

Use standard nose and clean up incantation

Fix travis yml syntax error

Put in miniconda workout for libc error.

Fix imread plugin.

Fix travis syntax

Remove unused import

Remove miniconda libpng in favor of system png

Fix imread install and move libm removal to after optional pkg install.

Fix png header copy in travis yml

Another attempt to use png headers

Debug freeimage

Add jpeg library for freeimage and debug imread.

More debug for imread and freeimage

More freeimage and imread debugging

More debugging

Use correct paths for test env

Make sure imread is tied to libpng15

Add a TODO note for simpleitk test causing error.

Fix typo in yml

Cleanup and add more comments to travis yml

Update comment

Try and add 3.2 support.

Docstring formatting

Add more travis comments.

Try numpy 1.6 on python 2.7

Fix travis syntax error

Rename CONDA to ENV for clarity

Alias python on python 3.2

Use python 3.2 as the system python

Clean up libfreeimage install

Fix order on py3.2 pre_install

Move old numpy back to py26

Use the appropriate python calls.

Debug 3.2 build.

Update comment

Fix syntax error

Another fix for syntax error.

Install scipy after downloading import tools

More debugging for py32

Do not install conda on py3.2 (duh)

Fix typo in travis yml

Fix py32 qt install, separate pyfits and imread to find error

Fix syntax error and front-load option lib check for debug

pyfits is not supported in py3.2, try imread now

imread is also not supported on py3.2

install imread before pyfits to show relationship with libs

Make pip builds quiet

Minor formatting to retrigger build

Allow simpleitk to fail to download without breaking the build

Use travis_retry for SimpleITK

See what breaks when we keep libm in

Now remove libm again
2014-08-02 06:47:09 -05:00

214 lines
7.4 KiB
Python

try:
from matplotlib.widgets import RectangleSelector
except ImportError:
RectangleSelector = object
from skimage.viewer.canvastools.base import CanvasToolBase
from skimage.viewer.canvastools.base import ToolHandles
__all__ = ['RectangleTool']
class RectangleTool(CanvasToolBase, RectangleSelector):
"""Widget for selecting a rectangular region in a plot.
After making the desired selection, press "Enter" to accept the selection
and call the `on_enter` callback function.
Parameters
----------
ax : :class:`matplotlib.axes.Axes`
Matplotlib axes where tool is displayed.
on_move : function
Function called whenever a control handle is moved.
This function must accept the rectangle extents as the only argument.
on_release : function
Function called whenever the control handle is released.
on_enter : function
Function called whenever the "enter" key is pressed.
maxdist : float
Maximum pixel distance allowed when selecting control handle.
rect_props : dict
Properties for :class:`matplotlib.patches.Rectangle`. This class
redefines defaults in :class:`matplotlib.widgets.RectangleSelector`.
Attributes
----------
extents : tuple
Rectangle extents: (xmin, xmax, ymin, ymax).
"""
def __init__(self, ax, on_move=None, on_release=None, on_enter=None,
maxdist=10, rect_props=None):
CanvasToolBase.__init__(self, ax, on_move=on_move,
on_enter=on_enter, on_release=on_release)
props = dict(edgecolor=None, facecolor='r', alpha=0.15)
props.update(rect_props if rect_props is not None else {})
if props['edgecolor'] is None:
props['edgecolor'] = props['facecolor']
RectangleSelector.__init__(self, ax, lambda *args: None,
rectprops=props,
useblit=self.useblit)
# Alias rectangle attribute, which is initialized in RectangleSelector.
self._rect = self.to_draw
self._rect.set_animated(True)
self.maxdist = maxdist
self.active_handle = None
self._extents_on_press = None
if on_enter is None:
def on_enter(extents):
print("(xmin=%.3g, xmax=%.3g, ymin=%.3g, ymax=%.3g)" % extents)
self.callback_on_enter = on_enter
props = dict(mec=props['edgecolor'])
self._corner_order = ['NW', 'NE', 'SE', 'SW']
xc, yc = self.corners
self._corner_handles = ToolHandles(ax, xc, yc, marker_props=props)
self._edge_order = ['W', 'N', 'E', 'S']
xe, ye = self.edge_centers
self._edge_handles = ToolHandles(ax, xe, ye, marker='s',
marker_props=props)
self._artists = [self._rect,
self._corner_handles.artist,
self._edge_handles.artist]
@property
def _rect_bbox(self):
x0 = self._rect.get_x()
y0 = self._rect.get_y()
width = self._rect.get_width()
height = self._rect.get_height()
return x0, y0, width, height
@property
def corners(self):
"""Corners of rectangle from lower left, moving clockwise."""
x0, y0, width, height = self._rect_bbox
xc = x0, x0 + width, x0 + width, x0
yc = y0, y0, y0 + height, y0 + height
return xc, yc
@property
def edge_centers(self):
"""Midpoint of rectangle edges from left, moving clockwise."""
x0, y0, width, height = self._rect_bbox
w = width / 2.
h = height / 2.
xe = x0, x0 + w, x0 + width, x0 + w
ye = y0 + h, y0, y0 + h, y0 + height
return xe, ye
@property
def extents(self):
"""Return (xmin, xmax, ymin, ymax)."""
x0, y0, width, height = self._rect_bbox
xmin, xmax = sorted([x0, x0 + width])
ymin, ymax = sorted([y0, y0 + height])
return xmin, xmax, ymin, ymax
@extents.setter
def extents(self, extents):
x1, x2, y1, y2 = extents
xmin, xmax = sorted([x1, x2])
ymin, ymax = sorted([y1, y2])
# Update displayed rectangle
self._rect.set_x(xmin)
self._rect.set_y(ymin)
self._rect.set_width(xmax - xmin)
self._rect.set_height(ymax - ymin)
# Update displayed handles
self._corner_handles.set_data(*self.corners)
self._edge_handles.set_data(*self.edge_centers)
self.set_visible(True)
self.redraw()
def release(self, event):
if event.button != 1:
return
if not self.ax.in_axes(event):
self.eventpress = None
return
RectangleSelector.release(self, event)
self._extents_on_press = None
# Undo hiding of rectangle and redraw.
self.set_visible(True)
self.redraw()
self.callback_on_release(self.geometry)
def press(self, event):
if event.button != 1 or not self.ax.in_axes(event):
return
self._set_active_handle(event)
if self.active_handle is None:
# Clear previous rectangle before drawing new rectangle.
self.set_visible(False)
self.redraw()
self.set_visible(True)
RectangleSelector.press(self, event)
def _set_active_handle(self, event):
"""Set active handle based on the location of the mouse event"""
# Note: event.xdata/ydata in data coordinates, event.x/y in pixels
c_idx, c_dist = self._corner_handles.closest(event.x, event.y)
e_idx, e_dist = self._edge_handles.closest(event.x, event.y)
# Set active handle as closest handle, if mouse click is close enough.
if c_dist > self.maxdist and e_dist > self.maxdist:
self.active_handle = None
return
elif c_dist < e_dist:
self.active_handle = self._corner_order[c_idx]
else:
self.active_handle = self._edge_order[e_idx]
# Save coordinates of rectangle at the start of handle movement.
x1, x2, y1, y2 = self.extents
# Switch variables so that only x2 and/or y2 are updated on move.
if self.active_handle in ['W', 'SW', 'NW']:
x1, x2 = x2, event.xdata
if self.active_handle in ['N', 'NW', 'NE']:
y1, y2 = y2, event.ydata
self._extents_on_press = x1, x2, y1, y2
def onmove(self, event):
if self.eventpress is None or not self.ax.in_axes(event):
return
if self.active_handle is None:
# New rectangle
x1 = self.eventpress.xdata
y1 = self.eventpress.ydata
x2, y2 = event.xdata, event.ydata
else:
x1, x2, y1, y2 = self._extents_on_press
if self.active_handle in ['E', 'W'] + self._corner_order:
x2 = event.xdata
if self.active_handle in ['N', 'S'] + self._corner_order:
y2 = event.ydata
self.extents = (x1, x2, y1, y2)
self.callback_on_move(self.geometry)
@property
def geometry(self):
return self.extents
if __name__ == '__main__': # pragma: no cover
import matplotlib.pyplot as plt
from skimage import data
f, ax = plt.subplots()
ax.imshow(data.camera(), interpolation='nearest')
rect_tool = RectangleTool(ax)
plt.show()
print("Final selection:")
rect_tool.callback_on_enter(rect_tool.extents)