Skip to content

Commit 8111a3a

Browse files
committed
Applied Paul's pick restructure pick contains patch
svn path=/trunk/matplotlib/; revision=3487
1 parent b79039a commit 8111a3a

14 files changed

+595
-155
lines changed

CHANGELOG

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
2007-07-09 Applied Paul's pick restructure pick and add pickers,
2+
sourceforge patch 1749829 - JDH
3+
4+
15
2007-07-09 Applied Allan's draw_lines agg optimization. JDH
26

37

lib/matplotlib/artist.py

+62-5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def __init__(self):
4242
self._lod = False
4343
self._label = ''
4444
self._picker = None
45+
self._contains = None
4546

4647
self.eventson = False # fire events only if eventson
4748
self._oid = 0 # an observer id
@@ -120,6 +121,49 @@ def get_transform(self):
120121
self._transform = identity_transform()
121122
return self._transform
122123

124+
def hitlist(self,event):
125+
"""List the children of the artist which contain the mouse event"""
126+
import traceback
127+
L = []
128+
try:
129+
hascursor,info = self.contains(event)
130+
if hascursor:
131+
L.append(self)
132+
except:
133+
traceback.print_exc()
134+
print "while checking",self.__class__
135+
136+
if hasattr(self,'get_children'):
137+
for a in self.get_children(): L.extend(a.hitlist(event))
138+
return L
139+
140+
def contains(self,mouseevent):
141+
"""Test whether the artist contains the mouse event.
142+
143+
Returns the truth value and a dictionary of artist specific details of
144+
selection, such as which points are contained in the pick radius. See
145+
individual artists for details.
146+
"""
147+
if callable(self._contains): return self._contains(self,mouseevent)
148+
#raise NotImplementedError,str(self.__class__)+" needs 'contains' method"
149+
print str(self.__class__)+" needs 'contains' method"
150+
return False,{}
151+
152+
def set_contains(self,picker):
153+
"""Replace the contains test used by this artist. The new picker should
154+
be a callable function which determines whether the artist is hit by the
155+
mouse event:
156+
157+
hit, props = picker(artist, mouseevent)
158+
159+
If the mouse event is over the artist, return hit=True and props
160+
is a dictionary of properties you want returned with the contains test.
161+
"""
162+
self._contains = picker
163+
164+
def get_contains(self):
165+
'return the _contains test used by the artist, or None for default.'
166+
return self._contains
123167

124168
def pickable(self):
125169
'return True if self is pickable'
@@ -129,11 +173,24 @@ def pickable(self):
129173

130174
def pick(self, mouseevent):
131175
"""
132-
the user picked location x,y; if this Artist is within picker
133-
"pick epsilon" of x,y fire off a pick event
176+
pick(mouseevent)
177+
178+
each child artist will fire a pick event if mouseevent is over
179+
the artist and the artist has picker set
134180
"""
135-
# if mouseevent x, y are within picker call self.figure.canvas.pick_event(self, mouseevent)
136-
pass
181+
# Pick self
182+
if self.pickable():
183+
picker = self.get_picker()
184+
if callable(picker):
185+
inside,prop = picker(self,mouseevent)
186+
else:
187+
inside,prop = self.contains(mouseevent)
188+
if inside:
189+
self.figure.canvas.pick_event(mouseevent, self, **prop)
190+
191+
# Pick children
192+
if hasattr(self,'get_children'):
193+
for a in self.get_children(): a.pick(mouseevent)
137194

138195
def set_picker(self, picker):
139196
"""
@@ -367,7 +424,7 @@ def set(self, **kwargs):
367424

368425
class ArtistInspector:
369426
"""
370-
A helper class to insect an Artist and return information about
427+
A helper class to inspect an Artist and return information about
371428
it's settable properties and their current values
372429
"""
373430
def __init__(self, o):

lib/matplotlib/axes.py

+41-10
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ class Axes(Artist):
460460
scaled = {IDENTITY : 'linear',
461461
LOG10 : 'log',
462462
}
463+
464+
def __str__(self):
465+
return "Axes(%g,%g;%gx%g)"%(self._position[0].get(),self._position[1].get(),
466+
self._position[2].get(),self._position[3].get())
463467
def __init__(self, fig, rect,
464468
axisbg = None, # defaults to rc axes.facecolor
465469
frameon = True,
@@ -785,7 +789,8 @@ def cla(self):
785789
self.axesPatch.set_linewidth(rcParams['axes.linewidth'])
786790
self.axesFrame = Line2D((0,1,1,0,0), (0,0,1,1,0),
787791
linewidth=rcParams['axes.linewidth'],
788-
color=rcParams['axes.edgecolor'])
792+
color=rcParams['axes.edgecolor'],
793+
figure=self.figure)
789794
self.axesFrame.set_transform(self.transAxes)
790795
self.axesFrame.set_zorder(2.5)
791796
self.axison = True
@@ -1948,7 +1953,18 @@ def get_children(self):
19481953
children.append(self.axesFrame)
19491954
return children
19501955

1951-
def pick(self, *args):
1956+
def contains(self,mouseevent):
1957+
"""Test whether the mouse event occured in the axes.
1958+
1959+
Returns T/F, {}
1960+
"""
1961+
if callable(self._contains): return self._contains(self,mouseevent)
1962+
1963+
x,y = self.axes.transAxes.inverse_xy_tup((mouseevent.x,mouseevent.y))
1964+
inside = x>=0 and x<=1 and y>=0 and y<=1
1965+
return inside,{}
1966+
1967+
def pick(self,*args):
19521968
"""
19531969
pick(mouseevent)
19541970
@@ -1957,11 +1973,7 @@ def pick(self, *args):
19571973
"""
19581974
if len(args)>1:
19591975
raise DeprecationWarning('New pick API implemented -- see API_CHANGES in the src distribution')
1960-
mouseevent = args[0]
1961-
for a in self.get_children():
1962-
a.pick(mouseevent)
1963-
1964-
1976+
Artist.pick(self,args[0])
19651977

19661978
def __pick(self, x, y, trans=None, among=None):
19671979
"""
@@ -5299,6 +5311,9 @@ class Subplot(SubplotBase, Axes):
52995311
53005312
Subplot(211) # 2 rows, 1 column, first (upper) plot
53015313
"""
5314+
def __str__(self):
5315+
return "Subplot(%g,%g)"%(self.bottom.get(),self.left.get())
5316+
53025317
def __init__(self, fig, *args, **kwargs):
53035318
"""
53045319
See Axes base class documentation for args and kwargs
@@ -5363,6 +5378,18 @@ def _set_lim_and_transforms(self):
53635378
self.transAxes = get_bbox_transform(unit_bbox(), self.bbox)
53645379

53655380

5381+
def contains(self,mouseevent):
5382+
"""Test whether the mouse event occured in the axes.
5383+
5384+
Returns T/F, {}
5385+
"""
5386+
if callable(self._contains): return self._contains(self,mouseevent)
5387+
5388+
x,y = self.axes.transAxes.inverse_xy_tup((mouseevent.x,mouseevent.y))
5389+
#print "Polar: x,y = ",x,y
5390+
inside = (x-0.5)**2 + (y-0.5)**2 <= 0.25
5391+
return inside,{}
5392+
53665393
def cla(self):
53675394
'Clear the current axes'
53685395

@@ -5534,9 +5561,10 @@ def set_rgrids(self, radii, labels=None, angle=22.5, rpad=0.05, **kwargs):
55345561
rmax = self.get_rmax()
55355562
for r in radii:
55365563
r = ones(self.RESOLUTION)*r
5537-
line = Line2D(theta, r, linestyle=ls, color=color, linewidth=lw)
5564+
line = Line2D(theta, r, linestyle=ls, color=color, linewidth=lw,
5565+
figure=self.figure)
55385566
#line = Line2D(nx.mlab.rand(len(theta)), nx.mlab.rand(len(theta)),
5539-
# linestyle=ls, color=color, linewidth=lw)
5567+
# linestyle=ls, color=color, linewidth=lw, figure=self.figure)
55405568
line.set_transform(self.transData)
55415569
self.rgridlines.append(line)
55425570

@@ -5596,7 +5624,8 @@ def set_thetagrids(self, angles, labels=None, fmt='%d', frac = 1.1,
55965624
r = linspace(0., rmax, self.RESOLUTION)
55975625
for a in angles:
55985626
theta = ones(self.RESOLUTION)*a/180.*math.pi
5599-
line = Line2D(theta, r, linestyle=ls, color=color, linewidth=lw)
5627+
line = Line2D(theta, r, linestyle=ls, color=color, linewidth=lw,
5628+
figure=self.figure)
56005629
line.set_transform(self.transData)
56015630
self.thetagridlines.append(line)
56025631

@@ -5779,6 +5808,8 @@ class PolarSubplot(SubplotBase, PolarAxes):
57795808
57805809
Subplot(211) # 2 rows, 1 column, first (upper) plot
57815810
"""
5811+
def __str__(self):
5812+
return "PolarSubplot(%gx%g)"%(self.figW,self.figH)
57825813
def __init__(self, fig, *args, **kwargs):
57835814
SubplotBase.__init__(self, fig, *args)
57845815
PolarAxes.__init__(self, fig, [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs)

lib/matplotlib/axis.py

+55-19
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,14 @@ def get_children(self):
116116
children = [self.tick1line, self.tick2line, self.gridline, self.label1, self.label2]
117117
return children
118118

119-
def pick(self, mouseevent):
119+
def contains(self, mouseevent):
120+
"""Test whether the mouse event occured in the Tick marks.
121+
122+
This function always returns false. It is more useful to test if the
123+
axis as a whole contains the mouse rather than the set of tick marks.
120124
"""
121-
pick(mouseevent)
122-
123-
each child artist will fire a pick event if mouseevent is over
124-
the artist and the artist has picker set
125-
"""
126-
for a in self.get_children():
127-
a.pick(mouseevent)
125+
if callable(self._contains): return self._contains(self,mouseevent)
126+
return False,{}
128127

129128
def set_pad(self, val):
130129
"""
@@ -493,7 +492,11 @@ class Axis(Artist):
493492
LABELPAD = 5
494493
OFFSETTEXTPAD = 3
495494

496-
def __init__(self, axes):
495+
def __str__(self):
496+
return str(self.__class__).split('.')[-1] \
497+
+ "(%d,%d)"%self.axes.transAxes.xy_tup((0,0))
498+
499+
def __init__(self, axes, pickradius=15):
497500
"""
498501
Init the axis with the parent Axes instance
499502
"""
@@ -515,6 +518,7 @@ def __init__(self, axes):
515518
self.offsetText = self._get_offset_text()
516519
self.majorTicks = []
517520
self.minorTicks = []
521+
self.pickradius = pickradius
518522

519523
self.cla()
520524

@@ -524,16 +528,6 @@ def get_children(self):
524528
children.extend(self.minorTicks)
525529
return children
526530

527-
def pick(self, mouseevent):
528-
"""
529-
pick(mouseevent)
530-
531-
each child artist will fire a pick event if mouseevent is over
532-
the artist and the artist has picker set
533-
"""
534-
for a in self.get_children():
535-
a.pick(mouseevent)
536-
537531
def cla(self):
538532
'clear the current axis'
539533
self.set_major_locator(AutoLocator())
@@ -663,6 +657,10 @@ def get_label(self):
663657
def get_offset_text(self):
664658
'Return the axis offsetText as a Text instance'
665659
return self.offsetText
660+
661+
def get_pickradius(self):
662+
'Return the depth of the axis used by the picker'
663+
return self.pickradius
666664

667665
def get_ticklabels(self):
668666
'Return a list of Text instances for ticklabels'
@@ -903,6 +901,14 @@ def set_minor_locator(self, locator):
903901
self.minor.locator = locator
904902
self.minor.locator.set_view_interval( self.get_view_interval() )
905903
self.minor.locator.set_data_interval( self.get_data_interval() )
904+
905+
def set_pickradius(self, pickradius):
906+
"""
907+
Set the depth of the axis used by the picker
908+
909+
ACCEPTS: a distance in points
910+
"""
911+
self.pickradius = pickradius
906912

907913

908914
def set_ticklabels(self, ticklabels, *args, **kwargs):
@@ -961,6 +967,20 @@ def zoom(self, direction):
961967

962968
class XAxis(Axis):
963969
__name__ = 'xaxis'
970+
971+
def contains(self,mouseevent):
972+
"""Test whether the mouse event occured in the x axis.
973+
"""
974+
if callable(self._contains): return self._contains(self,mouseevent)
975+
976+
xpixel,ypixel = mouseevent.x,mouseevent.y
977+
try:
978+
xaxes,yaxes = self.axes.transAxes.inverse_xy_tup((xpixel,ypixel))
979+
xorigin,yorigin = self.axes.transAxes.xy_tup((0,0))
980+
except ValueError:
981+
return False, {}
982+
inaxis = xaxes>=0 and xaxes<=1 and ypixel<yorigin and ypixel>yorigin-self.pickradius
983+
return inaxis, {}
964984

965985
def _get_tick(self, major):
966986
return XTick(self.axes, 0, '', major=major)
@@ -1133,6 +1153,22 @@ def get_data_interval(self):
11331153
class YAxis(Axis):
11341154
__name__ = 'yaxis'
11351155

1156+
def contains(self,mouseevent):
1157+
"""Test whether the mouse event occurred in the y axis.
1158+
1159+
Returns T/F, {}
1160+
"""
1161+
if callable(self._contains): return self._contains(self,mouseevent)
1162+
1163+
xpixel,ypixel = mouseevent.x,mouseevent.y
1164+
try:
1165+
xaxes,yaxes = self.axes.transAxes.inverse_xy_tup((xpixel,ypixel))
1166+
xorigin,yorigin = self.axes.transAxes.xy_tup((0,0))
1167+
except ValueError:
1168+
return False, {}
1169+
inaxis = yaxes>=0 and yaxes<=1 and xpixel<xorigin and xpixel>xorigin-self.pickradius
1170+
return inaxis, {}
1171+
11361172
def _get_tick(self, major):
11371173
return YTick(self.axes, 0, '', major=major)
11381174

0 commit comments

Comments
 (0)