3737import os
3838import sys
3939import time
40+ import weakref
4041from weakref import WeakKeyDictionary
4142
4243import numpy as np
@@ -1331,7 +1332,11 @@ class LocationEvent(Event):
13311332 The keyboard modifiers currently being pressed (except for KeyEvent).
13321333 """
13331334
1334- lastevent = None # The last event processed so far.
1335+ # Fully delete all occurrences of lastevent after deprecation elapses.
1336+ _lastevent = None
1337+ lastevent = _api .deprecated ("3.8" )(
1338+ _api .classproperty (lambda cls : cls ._lastevent ))
1339+ _last_axes_ref = None
13351340
13361341 def __init__ (self , name , canvas , x , y , guiEvent = None , * , modifiers = None ):
13371342 super ().__init__ (name , canvas , guiEvent = guiEvent )
@@ -1348,20 +1353,25 @@ def __init__(self, name, canvas, x, y, guiEvent=None, *, modifiers=None):
13481353 # cannot check if event was in Axes if no (x, y) info
13491354 return
13501355
1351- if self .canvas .mouse_grabber is None :
1352- self .inaxes = self .canvas .inaxes ((x , y ))
1353- else :
1354- self .inaxes = self .canvas .mouse_grabber
1356+ self ._set_inaxes (self .canvas .inaxes ((x , y ))
1357+ if self .canvas .mouse_grabber is None else
1358+ self .canvas .mouse_grabber ,
1359+ (x , y ))
1360+
1361+ # Splitting _set_inaxes out is useful for the axes_leave_event handler: it
1362+ # needs to generate synthetic LocationEvents with manually-set inaxes. In
1363+ # that latter case, xy has already been cast to int so it can directly be
1364+ # read from self.x, self.y; in the normal case, however, it is more
1365+ # accurate to pass the untruncated float x, y values passed to the ctor.
13551366
1356- if self .inaxes is not None :
1367+ def _set_inaxes (self , inaxes , xy = None ):
1368+ self .inaxes = inaxes
1369+ if inaxes is not None :
13571370 try :
1358- trans = self .inaxes .transData .inverted ()
1359- xdata , ydata = trans . transform (( x , y ))
1371+ self . xdata , self .ydata = inaxes .transData .inverted (). transform (
1372+ xy if xy is not None else ( self . x , self . y ))
13601373 except ValueError :
13611374 pass
1362- else :
1363- self .xdata = xdata
1364- self .ydata = ydata
13651375
13661376
13671377class MouseButton (IntEnum ):
@@ -1555,17 +1565,30 @@ def _mouse_handler(event):
15551565 event .key = event .canvas ._key
15561566 # Emit axes_enter/axes_leave.
15571567 if event .name == "motion_notify_event" :
1558- last = LocationEvent .lastevent
1559- last_axes = last . inaxes if last is not None else None
1568+ last_ref = LocationEvent ._last_axes_ref
1569+ last_axes = last_ref () if last_ref else None
15601570 if last_axes != event .inaxes :
15611571 if last_axes is not None :
1572+ # Create a synthetic LocationEvent for the axes_leave_event.
1573+ # Its inaxes attribute needs to be manually set (because the
1574+ # cursor is actually *out* of that axes at that point); this is
1575+ # done with the internal _set_inaxes method which ensures that
1576+ # the xdata and ydata attributes are also correct.
15621577 try :
1563- last .canvas .callbacks .process ("axes_leave_event" , last )
1578+ leave_event = LocationEvent (
1579+ "axes_leave_event" , last_axes .figure .canvas ,
1580+ event .x , event .y , event .guiEvent ,
1581+ modifiers = event .modifiers )
1582+ leave_event ._set_inaxes (last_axes )
1583+ last_axes .figure .canvas .callbacks .process (
1584+ "axes_leave_event" , leave_event )
15641585 except Exception :
15651586 pass # The last canvas may already have been torn down.
15661587 if event .inaxes is not None :
15671588 event .canvas .callbacks .process ("axes_enter_event" , event )
1568- LocationEvent .lastevent = (
1589+ LocationEvent ._last_axes_ref = (
1590+ weakref .ref (event .inaxes ) if event .inaxes else None )
1591+ LocationEvent ._lastevent = (
15691592 None if event .name == "figure_leave_event" else event )
15701593
15711594
@@ -1964,8 +1987,8 @@ def leave_notify_event(self, guiEvent=None):
19641987 guiEvent
19651988 The native UI event that generated the Matplotlib event.
19661989 """
1967- self .callbacks .process ('figure_leave_event' , LocationEvent .lastevent )
1968- LocationEvent .lastevent = None
1990+ self .callbacks .process ('figure_leave_event' , LocationEvent ._lastevent )
1991+ LocationEvent ._lastevent = None
19691992 self ._lastx , self ._lasty = None , None
19701993
19711994 @_api .deprecated ("3.6" , alternative = (
0 commit comments