Skip to content

Commit 8345d4c

Browse files
committed
Add LassoSelector widget with demo.
Note: I put the demo in the "widgets" directory even though the `Lasso` demo is in "event_handling".
1 parent 0f0286e commit 8345d4c

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import numpy as np
2+
3+
from matplotlib.widgets import LassoSelector
4+
from matplotlib.nxutils import points_inside_poly
5+
6+
7+
class SelectFromCollection(object):
8+
"""Select indices from a matplotlib collection using :class:`LassoSelector`.
9+
10+
Selected indices are saved in the `ind` attribute. This tool highlights
11+
selected points by fading them out (i.e., reducing their alpha values).
12+
If your collection has alpha < 1, this tool will permanently alter them.
13+
14+
Note that this tool selects collection objects based on their *origins*
15+
(i.e., `offsets`).
16+
17+
Parameters
18+
----------
19+
ax : :class:`Axes`
20+
Axes to interact with.
21+
22+
collection : :class:`Collection`
23+
Collection you want to select from.
24+
25+
alpha_other : 0 <= float <= 1
26+
To highlight a selection, this tool sets all selected points to an
27+
alpha value of 1 and non-selected points to `alpha_other`.
28+
"""
29+
def __init__(self, ax, collection, alpha_other=0.3):
30+
self.canvas = ax.figure.canvas
31+
self.collection = collection
32+
self.alpha_other = alpha_other
33+
34+
self.xys = collection.get_offsets()
35+
self.Npts = len(self.xys)
36+
37+
# Ensure that we have separate colors for each object
38+
self.fc = collection.get_facecolors()
39+
if len(self.fc) == 0:
40+
raise ValueError('Collection must have a facecolor')
41+
elif len(self.fc) == 1:
42+
self.fc = np.tile(self.fc, self.Npts).reshape(self.Npts, -1)
43+
44+
self.lasso = LassoSelector(ax, onselect=self.onselect)
45+
self.ind = []
46+
47+
def onselect(self, verts):
48+
self.ind = np.nonzero(points_inside_poly(self.xys, verts))[0]
49+
self.fc[:, -1] = self.alpha_other
50+
self.fc[self.ind, -1] = 1
51+
self.collection.set_facecolors(self.fc)
52+
self.canvas.draw_idle()
53+
54+
def disconnect(self):
55+
self.lasso.disconnect_events()
56+
self.fc[:, -1] = 1
57+
self.collection.set_facecolors(self.fc)
58+
self.canvas.draw_idle()
59+
60+
61+
if __name__ == '__main__':
62+
import matplotlib.pyplot as plt
63+
64+
plt.ion()
65+
data = np.random.rand(100, 2)
66+
67+
subplot_kw = dict(xlim=(0,1), ylim=(0,1), autoscale_on=False)
68+
fig, ax = plt.subplots(subplot_kw=subplot_kw)
69+
70+
pts = ax.scatter(data[:, 0], data[:, 1], s=80)
71+
selector = SelectFromCollection(ax, pts)
72+
73+
plt.draw()
74+
raw_input('Press any key to accept selected points')
75+
print "Selected points:"
76+
print selector.xys[selector.ind]
77+
selector.disconnect()
78+
79+
# Block end of script so you can check that lasso is disconnected.
80+
raw_input('Press any key to quit')
81+

lib/matplotlib/widgets.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,71 @@ def get_active(self):
14091409
""" Get status of active mode (boolean variable)"""
14101410
return self.active
14111411

1412+
1413+
class LassoSelector(AxesWidget):
1414+
def __init__(self, ax, onselect=None, useblit=True, lineprops=None):
1415+
AxesWidget.__init__(self, ax)
1416+
1417+
self.useblit = useblit
1418+
self.onselect = onselect
1419+
self.verts = None
1420+
1421+
if lineprops is None:
1422+
lineprops = dict()
1423+
self.line = Line2D([], [], **lineprops)
1424+
self.line.set_visible(False)
1425+
self.ax.add_line(self.line)
1426+
1427+
self.connect_event('button_press_event', self.onpress)
1428+
self.connect_event('button_release_event', self.onrelease)
1429+
self.connect_event('motion_notify_event', self.onmove)
1430+
self.connect_event('draw_event', self.update_background)
1431+
1432+
def ignore(self, event):
1433+
wrong_button = hasattr(event, 'button') and event.button != 1
1434+
return not self.active or wrong_button
1435+
1436+
def onpress(self, event):
1437+
if self.ignore(event) or event.inaxes != self.ax:
1438+
return
1439+
self.verts = [(event.xdata, event.ydata)]
1440+
self.line.set_visible(True)
1441+
1442+
def onrelease(self, event):
1443+
if self.ignore(event):
1444+
return
1445+
if self.verts is not None:
1446+
if event.inaxes == self.ax:
1447+
self.verts.append((event.xdata, event.ydata))
1448+
self.onselect(self.verts)
1449+
self.line.set_data([[], []])
1450+
self.line.set_visible(False)
1451+
self.verts = None
1452+
1453+
def onmove(self, event):
1454+
if self.ignore(event) or event.inaxes != self.ax:
1455+
return
1456+
if self.verts is None: return
1457+
if event.inaxes != self.ax: return
1458+
if event.button!=1: return
1459+
self.verts.append((event.xdata, event.ydata))
1460+
1461+
self.line.set_data(zip(*self.verts))
1462+
1463+
if self.useblit:
1464+
self.canvas.restore_region(self.background)
1465+
self.ax.draw_artist(self.line)
1466+
self.canvas.blit(self.ax.bbox)
1467+
else:
1468+
self.canvas.draw_idle()
1469+
1470+
def update_background(self, event):
1471+
if self.ignore(event):
1472+
return
1473+
if self.useblit:
1474+
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
1475+
1476+
14121477
class Lasso(AxesWidget):
14131478
def __init__(self, ax, xy, callback=None, useblit=True):
14141479
AxesWidget.__init__(self, ax)
@@ -1452,3 +1517,4 @@ def onmove(self, event):
14521517
self.canvas.blit(self.ax.bbox)
14531518
else:
14541519
self.canvas.draw_idle()
1520+

0 commit comments

Comments
 (0)