3
3
4
4
import numpy as np
5
5
6
+ from matplotlib .backend_bases import PickEvent
7
+ import matplotlib .artist as martist
8
+
6
9
from .containers import DataContainer , ArrayContainer , DataUnion
7
10
from .description import Desc , desc_like
8
- from .conversion_edge import Edge , Graph , TransformEdge
11
+ from .conversion_edge import Edge , FuncEdge , Graph , TransformEdge
9
12
10
13
11
14
class Artist :
@@ -18,6 +21,9 @@ def __init__(
18
21
kwargs_cont = ArrayContainer (** kwargs )
19
22
self ._container = DataUnion (container , kwargs_cont )
20
23
24
+ self ._children : list [tuple [float , Artist ]] = []
25
+ self ._picker = None
26
+
21
27
edges = edges or []
22
28
self ._visible = True
23
29
self ._graph = Graph (edges )
@@ -41,6 +47,77 @@ def get_visible(self):
41
47
def set_visible (self , visible ):
42
48
self ._visible = visible
43
49
50
+ def pickable (self ) -> bool :
51
+ return self ._picker is not None
52
+
53
+ def get_picker (self ):
54
+ return self ._picker
55
+
56
+ def set_picker (self , picker ):
57
+ self ._picker = picker
58
+
59
+ def contains (self , mouseevent , graph = None ):
60
+ """
61
+ Test whether the artist contains the mouse event.
62
+
63
+ Parameters
64
+ ----------
65
+ mouseevent : `~matplotlib.backend_bases.MouseEvent`
66
+
67
+ Returns
68
+ -------
69
+ contains : bool
70
+ Whether any values are within the radius.
71
+ details : dict
72
+ An artist-specific dictionary of details of the event context,
73
+ such as which points are contained in the pick radius. See the
74
+ individual Artist subclasses for details.
75
+ """
76
+ return False , {}
77
+
78
+ def get_children (self ):
79
+ return [a [1 ] for a in self ._children ]
80
+
81
+ def pick (self , mouseevent , graph : Graph | None = None ):
82
+ """
83
+ Process a pick event.
84
+
85
+ Each child artist will fire a pick event if *mouseevent* is over
86
+ the artist and the artist has picker set.
87
+
88
+ See Also
89
+ --------
90
+ set_picker, get_picker, pickable
91
+ """
92
+ if graph is None :
93
+ graph = self ._graph
94
+ else :
95
+ graph = graph + self ._graph
96
+ # Pick self
97
+ if self .pickable ():
98
+ picker = self .get_picker ()
99
+ if callable (picker ):
100
+ inside , prop = picker (self , mouseevent )
101
+ else :
102
+ inside , prop = self .contains (mouseevent , graph )
103
+ if inside :
104
+ PickEvent (
105
+ "pick_event" , mouseevent .canvas , mouseevent , self , ** prop
106
+ )._process ()
107
+
108
+ # Pick children
109
+ for a in self .get_children ():
110
+ # make sure the event happened in the same Axes
111
+ ax = getattr (a , "axes" , None )
112
+ if mouseevent .inaxes is None or ax is None or mouseevent .inaxes == ax :
113
+ # we need to check if mouseevent.inaxes is None
114
+ # because some objects associated with an Axes (e.g., a
115
+ # tick label) can be outside the bounding box of the
116
+ # Axes and inaxes will be None
117
+ # also check that ax is None so that it traverse objects
118
+ # which do not have an axes property but children might
119
+ a .pick (mouseevent , graph )
120
+
44
121
45
122
class CompatibilityArtist :
46
123
"""A compatibility shim to ducktype as a classic Matplotlib Artist.
@@ -59,7 +136,7 @@ class CompatibilityArtist:
59
136
useful for avoiding accidental dependency.
60
137
"""
61
138
62
- def __init__ (self , artist : Artist ):
139
+ def __init__ (self , artist : martist . Artist ):
63
140
self ._artist = artist
64
141
65
142
self ._axes = None
@@ -134,7 +211,7 @@ def draw(self, renderer, graph=None):
134
211
self ._artist .draw (renderer , graph + self ._graph )
135
212
136
213
137
- class CompatibilityAxes :
214
+ class CompatibilityAxes ( Artist ) :
138
215
"""A compatibility shim to add to traditional matplotlib axes.
139
216
140
217
At this time features are implemented on an "as needed" basis, and many
@@ -152,12 +229,11 @@ class CompatibilityAxes:
152
229
"""
153
230
154
231
def __init__ (self , axes ):
232
+ super ().__init__ (ArrayContainer ())
155
233
self ._axes = axes
156
234
self .figure = None
157
235
self ._clippath = None
158
- self ._visible = True
159
236
self .zorder = 2
160
- self ._children : list [tuple [float , Artist ]] = []
161
237
162
238
@property
163
239
def axes (self ):
@@ -187,6 +263,18 @@ def axes(self, ax):
187
263
desc_like (xy , coordinates = "display" ),
188
264
transform = self ._axes .transAxes ,
189
265
),
266
+ FuncEdge .from_func (
267
+ "xunits" ,
268
+ lambda : self ._axes .xaxis .units ,
269
+ {},
270
+ {"xunits" : Desc ((), "units" )},
271
+ ),
272
+ FuncEdge .from_func (
273
+ "yunits" ,
274
+ lambda : self ._axes .yaxis .units ,
275
+ {},
276
+ {"yunits" : Desc ((), "units" )},
277
+ ),
190
278
],
191
279
aliases = (("parent" , "axes" ),),
192
280
)
@@ -210,7 +298,7 @@ def get_animated(self):
210
298
return False
211
299
212
300
def draw (self , renderer , graph = None ):
213
- if not self .visible :
301
+ if not self .get_visible () :
214
302
return
215
303
if graph is None :
216
304
graph = Graph ([])
@@ -228,9 +316,3 @@ def set_xlim(self, min_=None, max_=None):
228
316
229
317
def set_ylim (self , min_ = None , max_ = None ):
230
318
self .axes .set_ylim (min_ , max_ )
231
-
232
- def get_visible (self ):
233
- return self ._visible
234
-
235
- def set_visible (self , visible ):
236
- self ._visible = visible
0 commit comments