Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Feature: Interactive Selector Tools #5786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 42 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ba8562b
Start base class for new interactive selector tools
blink1073 Jan 2, 2016
1e68194
Update docstring
blink1073 Jan 2, 2016
562d852
Cleanup
blink1073 Jan 2, 2016
2d580b3
Comment
blink1073 Jan 2, 2016
d57c49d
Clean up interactivity handling
blink1073 Jan 2, 2016
55e0de1
Minor cleanup
blink1073 Jan 2, 2016
940b014
Make interactive and allow_redraw attributes
blink1073 Jan 2, 2016
a8b2a94
Keep handles in sync even when not interactive
blink1073 Jan 2, 2016
e8e1807
Update docs
blink1073 Jan 2, 2016
4dec7c3
Clean up the clean event
blink1073 Jan 2, 2016
03a2e84
Implement focus/drag/selection logic
blink1073 Jan 2, 2016
3858737
Implement move logic
blink1073 Jan 2, 2016
a6e7af7
Improve handles handling
blink1073 Jan 2, 2016
9bf43c5
Stub out rectangle and ellipse tools
blink1073 Jan 2, 2016
fcc69a1
Finish rectangle, ellipse, and line tools
blink1073 Jan 2, 2016
91ca5e1
Use pixel space for line calculations and cleanups
blink1073 Jan 3, 2016
77c1122
Update documentation
blink1073 Jan 3, 2016
d37f0eb
Only update focus if not drawing
blink1073 Jan 3, 2016
ea77184
Update docstrings
blink1073 Jan 3, 2016
60a69a9
Make width optional for the line tool
blink1073 Jan 3, 2016
3240995
Fix initial size and reset behavior
blink1073 Jan 3, 2016
5bd0e12
Initialize _verts to valid value
blink1073 Jan 3, 2016
b8618b5
Remove self._verts since it was confusing
blink1073 Jan 3, 2016
6622c67
Fix base class docstring
blink1073 Jan 3, 2016
139b033
Fix the has_selected behavior
blink1073 Jan 3, 2016
56ea9cf
Improve handling of key modifiers
blink1073 Jan 3, 2016
b0f128f
Specify that the patch is a polygon
blink1073 Jan 3, 2016
f08b783
Fix handling of line width
blink1073 Jan 3, 2016
9e0afd6
Add the paint tool
blink1073 Jan 3, 2016
92b56ef
Make the patch private
blink1073 Jan 3, 2016
589eae8
Move polygon-specific items out of base tool
blink1073 Jan 3, 2016
0d999cb
wip
blink1073 Jan 9, 2016
6d84c47
wip
blink1073 Jan 9, 2016
fe672d6
wip
blink1073 Jan 9, 2016
ae1f67e
wip
blink1073 Jan 9, 2016
2de4037
wip
blink1073 Jan 9, 2016
5d89924
wip
blink1073 Jan 10, 2016
af58d64
wip
blink1073 Jan 11, 2016
4621bfd
Refactor the tools to use proper patches
blink1073 Jan 16, 2016
6781b9b
Clean up the line tool
blink1073 Jan 16, 2016
fb6b842
More cleanup
blink1073 Jan 16, 2016
f90d071
wip add angle
blink1073 Jan 23, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
wip add angle
  • Loading branch information
blink1073 committed Jan 23, 2016
commit f90d07121010e761a0821205ef6cc4fe3703ea17
84 changes: 55 additions & 29 deletions lib/matplotlib/interactive_selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import matplotlib.colors as mcolors
from matplotlib.patches import Patch, Rectangle, Ellipse, Polygon
from matplotlib.lines import Line2D
import matplotlib.transforms as transforms
from matplotlib import docstring, artist as martist


Expand Down Expand Up @@ -364,14 +365,18 @@ def __init__(self, ax, on_select=None, on_motion=None, on_accept=None,
self.patch = self._make_patch(**patch_props)
self.ax.add_patch(self.patch)

props = dict(marker='o', markersize=7, mfc='w', ls='none',
props = dict(marker='s', markersize=7, mfc='w', ls='none',
alpha=0.5, visible=False, label='_nolegend_',
picker=10, zorder=2)
props.update(handle_props or {})
self._handles = Line2D([], [], **props)
self.ax.add_line(self._handles)
self._size_handles = Line2D([], [], **props)
props['marker'] = 'o'
self._rot_handle = Line2D([], [], **props)

self._artists = [self.patch, self._handles]
self.ax.add_line(self._size_handles)
self.ax.add_line(self._rot_handle)

self._artists = [self.patch, self._size_handles, self._rot_handle]

self._interactive = None
self._modifiers = set()
Expand All @@ -394,7 +399,8 @@ def interactive(self):
@interactive.setter
def interactive(self, value):
if not value:
self._handles.set_visible(False)
self._size_handles.set_visible(False)
self._rot_handle.set_visible(False)
self.patch.set_visible(False)
self.canvas.draw_idle()
self._interactive = value
Expand All @@ -418,10 +424,14 @@ def set_geometry(self, **kwargs):
if self._prev_data is None:
self._prev_geometry = self.get_geometry()

handles = np.asarray(self._get_handle_verts())
self._handles.set_data(handles[:, 0], handles[:, 1])
self._handles.set_visible(self.interactive)
self._handles.set_animated(self._drawing)
size_size_handles, rot_handle = np.asarray(self._get_handle_verts())
self._size_handles.set_data(size_size_handles[:, 0], size_size_handles[:, 1])
self._size_handles.set_visible(self.interactive)
self._size_handles.set_animated(self._drawing)
if not rot_handle is None:
self._rot_handle.set_visible(self.interactive)
self._rot_handle.set_animated(self._drawing)
self._rot_handle.set_data(*rot_handle)
self._update()

if not self._drawing:
Expand All @@ -439,12 +449,13 @@ def _start_drawing(self, event):
for artist in self._artists:
artist.set_animated(self._useblit)
else:
self._handles.set_visible(False)
self._size_handles.set_visible(False)
self._rot_handle.set_visible(False)
# Blit without being visible if not dragging to avoid showing the old
# shape.
for artist in self._artists:
artist.set_visible(self._dragging)
if artist == self._handles:
if artist in [self._size_handles, self._rot_handle]:
artist.set_visible(self._dragging and self.interactive)
self._update()

Expand Down Expand Up @@ -472,14 +483,16 @@ def _handle_button_press(self, event):
self.focused = self.patch.contains(event)[0]

if self.interactive and not self._drawing:
self._dragging, idx = self._handles.contains(event)
self._dragging, idx = self._size_handles.contains(event)
if self._dragging:
self._drag_idx = idx['ind'][0]
elif self.patch.contains(event)[0]:
self._moving = True
self._dragging = True

if (self._drawing or self._dragging or self.allow_redraw or
self._rotating = self._rot_handle.contains(event)

if (self._drawing or self._dragging or self._rotating or self.allow_redraw or
not self._has_selected):
if self._moving:
self._start_drawing(event)
Expand Down Expand Up @@ -615,18 +628,25 @@ def _get_handle_verts(self):
width, height = geometry['width'], geometry['height']
if isinstance(self.patch, Ellipse):
xm, ym = geometry['center']
x0, y0 = xm - width / 2, ym - height / 2
else:
x0, y0 = geometry['xy']
xm, ym = x0 + width / 2, y0 + height / 2
w = width / 2
h = height / 2
xc = xm - w, xm + w, xm + w, xm - w
yc = ym - h, ym - h, ym + h, ym + h
xe = xm - w, xm, xm + w, xm
ye = ym, ym - h, ym, ym + h
xc = xm - w
yc = ym - h
xe = xm - w, xm
ye = ym, ym - h
x = np.hstack((xc, xe))
y = np.hstack((yc, ye))
return np.vstack((x, y)).T
pts = np.vstack((x, y)).T
rot_trans = transforms.Affine2D()
if isinstance(self.patch, Ellipse):
rot_trans.rotate_deg_around(xm, ym, geometry['angle'])
else:
rot_trans.rotate_deg_around(x0, y0, geometry['angle'])
return rot_trans.transform(pts), rot_trans.transform((xm, ym + h))

def _on_motion(self, event):
# Resize an existing shape.
Expand Down Expand Up @@ -756,7 +776,13 @@ def get_geometry(self):
end_points: The [(x0, y0), (x1, y1)] points.
width: The width of the tool in pixels.
"""
return dict(end_points=self.end_points, width=self.width)
verts = self.patch.get_xy()
p0x = (verts[0, 0] + verts[1, 0]) / 2
p0y = (verts[0, 1] + verts[1, 1]) / 2
p1x = (verts[3, 0] + verts[2, 0]) / 2
p1y = (verts[3, 1] + verts[2, 1]) / 2
pts = np.array([[p0x, p0y], [p1x, p1y]])
return dict(end_points=pts, width=self._width)

def set_geometry(self, end_points=None, width=None):
"""Set the geometry of the line tool.
Expand All @@ -768,9 +794,10 @@ def set_geometry(self, end_points=None, width=None):
width: int
The width in pixels of the line
"""
self.width = width or self.width
geometry = self.get_geometry()
self.width = width or geometry['width']
if end_points is None:
end_points = self.end_points
end_points = geometry['end_points']
pts = np.asarray(end_points)
# Get the widths in data units.
xfm = self.ax.transData.inverted()
Expand Down Expand Up @@ -802,15 +829,15 @@ def set_geometry(self, end_points=None, width=None):
super(LineTool, self).set_geometry(xy=(v00, v01, v11, v10))

def _make_patch(self, **overrides):
self.width = 1
self._width = 1
props = dict(facecolor='red', edgecolor='red', visible=False,
alpha=0.5, fill=True, picker=10, linewidth=2,
zorder=1)
props.update(overrides)
return Polygon([(0, 0), (0, 0), (0, 0), (0, 0)], **props)

def _get_handle_verts(self):
return self.end_points
return self.get_geometry()['end_points'], None

def _on_press(self, event):
if not self._dragging:
Expand All @@ -821,12 +848,12 @@ def _on_press(self, event):
self._start_drawing(event)

def _on_motion(self, event):
end_points = self.end_points
end_points = self.get_geometry()['end_points']
end_points[self._drag_idx, :] = event.xdata, event.ydata
self.set_geometry(end_points=end_points)

def _move(self, event):
pts = self.end_points
pts = self.get_geometry()['end_points']
pts[:, 0] += event.xdata - (pts[1, 0] + pts[0, 0]) / 2
pts[:, 1] += event.ydata - (pts[1, 1] + pts[0, 1]) / 2
self.set_geometry(end_points=pts)
Expand Down Expand Up @@ -1004,10 +1031,9 @@ def _update_cursor(self, x, y):
fig, ax = plt.subplots()

pts = ax.scatter(data[:, 0], data[:, 1], s=80)

"""
ellipse = RectangleTool(ax)
ellipse.set_geometry(xy=(0.4, 0.5), width=0.3, height=0.5)
ellipse = EllipseTool(ax)
ellipse.set_geometry(center=(0.4, 0.5), width=0.3, height=0.5, angle=45)
ax.invert_yaxis()

def test(tool):
Expand All @@ -1019,8 +1045,8 @@ def test(tool):
line = LineTool(ax)
line.set_geometry(end_points=[[0.1, 0.1], [0.5, 0.5]])
line.interactive = True

"""

def test(tool):
print(tool.overlay)

Expand Down