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

Skip to content

Commit 33a0599

Browse files
authored
Merge pull request #16931 from anntzer/reevents
Make it easier to improve UI event metadata.
2 parents c9a5463 + ff21516 commit 33a0599

15 files changed

+418
-327
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Event handlers
2+
~~~~~~~~~~~~~~
3+
The ``draw_event``, ``resize_event``, ``close_event``, ``key_press_event``,
4+
``key_release_event``, ``pick_event``, ``scroll_event``,
5+
``button_press_event``, ``button_release_event``, ``motion_notify_event``,
6+
``enter_notify_event`` and ``leave_notify_event`` methods of `.FigureCanvasBase`
7+
are deprecated. They had inconsistent signatures across backends, and made it
8+
difficult to improve event metadata.
9+
10+
In order to trigger an event on a canvas, directly construct an `.Event` object
11+
of the correct class and call ``canvas.callbacks.process(event.name, event)``.

lib/matplotlib/artist.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ def pick(self, mouseevent):
498498
--------
499499
set_picker, get_picker, pickable
500500
"""
501+
from .backend_bases import PickEvent # Circular import.
501502
# Pick self
502503
if self.pickable():
503504
picker = self.get_picker()
@@ -506,7 +507,8 @@ def pick(self, mouseevent):
506507
else:
507508
inside, prop = self.contains(mouseevent)
508509
if inside:
509-
self.figure.canvas.pick_event(mouseevent, self, **prop)
510+
PickEvent("pick_event", self.figure.canvas,
511+
mouseevent, self, **prop)._process()
510512

511513
# Pick children
512514
for a in self.get_children():

lib/matplotlib/backend_bases.py

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,11 +1220,16 @@ class Event:
12201220
guiEvent
12211221
The GUI event that triggered the Matplotlib event.
12221222
"""
1223+
12231224
def __init__(self, name, canvas, guiEvent=None):
12241225
self.name = name
12251226
self.canvas = canvas
12261227
self.guiEvent = guiEvent
12271228

1229+
def _process(self):
1230+
"""Generate an event with name ``self.name`` on ``self.canvas``."""
1231+
self.canvas.callbacks.process(self.name, self)
1232+
12281233

12291234
class DrawEvent(Event):
12301235
"""
@@ -1267,6 +1272,7 @@ class ResizeEvent(Event):
12671272
height : int
12681273
Height of the canvas in pixels.
12691274
"""
1275+
12701276
def __init__(self, name, canvas):
12711277
super().__init__(name, canvas)
12721278
self.width, self.height = canvas.get_width_height()
@@ -1294,7 +1300,7 @@ class LocationEvent(Event):
12941300
is not over an Axes.
12951301
"""
12961302

1297-
lastevent = None # the last event that was triggered before this one
1303+
lastevent = None # The last event processed so far.
12981304

12991305
def __init__(self, name, canvas, x, y, guiEvent=None):
13001306
super().__init__(name, canvas, guiEvent=guiEvent)
@@ -1308,7 +1314,6 @@ def __init__(self, name, canvas, x, y, guiEvent=None):
13081314

13091315
if x is None or y is None:
13101316
# cannot check if event was in Axes if no (x, y) info
1311-
self._update_enter_leave()
13121317
return
13131318

13141319
if self.canvas.mouse_grabber is None:
@@ -1326,34 +1331,6 @@ def __init__(self, name, canvas, x, y, guiEvent=None):
13261331
self.xdata = xdata
13271332
self.ydata = ydata
13281333

1329-
self._update_enter_leave()
1330-
1331-
def _update_enter_leave(self):
1332-
"""Process the figure/axes enter leave events."""
1333-
if LocationEvent.lastevent is not None:
1334-
last = LocationEvent.lastevent
1335-
if last.inaxes != self.inaxes:
1336-
# process Axes enter/leave events
1337-
try:
1338-
if last.inaxes is not None:
1339-
last.canvas.callbacks.process('axes_leave_event', last)
1340-
except Exception:
1341-
pass
1342-
# See ticket 2901582.
1343-
# I think this is a valid exception to the rule
1344-
# against catching all exceptions; if anything goes
1345-
# wrong, we simply want to move on and process the
1346-
# current event.
1347-
if self.inaxes is not None:
1348-
self.canvas.callbacks.process('axes_enter_event', self)
1349-
1350-
else:
1351-
# process a figure enter event
1352-
if self.inaxes is not None:
1353-
self.canvas.callbacks.process('axes_enter_event', self)
1354-
1355-
LocationEvent.lastevent = self
1356-
13571334

13581335
class MouseButton(IntEnum):
13591336
LEFT = 1
@@ -1375,11 +1352,15 @@ class MouseEvent(LocationEvent):
13751352
----------
13761353
button : None or `MouseButton` or {'up', 'down'}
13771354
The button pressed. 'up' and 'down' are used for scroll events.
1355+
13781356
Note that LEFT and RIGHT actually refer to the "primary" and
13791357
"secondary" buttons, i.e. if the user inverts their left and right
13801358
buttons ("left-handed setting") then the LEFT button will be the one
13811359
physically on the right.
13821360
1361+
If this is unset, *name* is "scroll_event", and *step* is nonzero, then
1362+
this will be set to "up" or "down" depending on the sign of *step*.
1363+
13831364
key : None or str
13841365
The key pressed when the mouse event triggered, e.g. 'shift'.
13851366
See `KeyEvent`.
@@ -1411,17 +1392,19 @@ def on_press(event):
14111392

14121393
def __init__(self, name, canvas, x, y, button=None, key=None,
14131394
step=0, dblclick=False, guiEvent=None):
1395+
super().__init__(name, canvas, x, y, guiEvent=guiEvent)
14141396
if button in MouseButton.__members__.values():
14151397
button = MouseButton(button)
1398+
if name == "scroll_event" and button is None:
1399+
if step > 0:
1400+
button = "up"
1401+
elif step < 0:
1402+
button = "down"
14161403
self.button = button
14171404
self.key = key
14181405
self.step = step
14191406
self.dblclick = dblclick
14201407

1421-
# super-init is deferred to the end because it calls back on
1422-
# 'axes_enter_event', which requires a fully initialized event.
1423-
super().__init__(name, canvas, x, y, guiEvent=guiEvent)
1424-
14251408
def __str__(self):
14261409
return (f"{self.name}: "
14271410
f"xy=({self.x}, {self.y}) xydata=({self.xdata}, {self.ydata}) "
@@ -1467,8 +1450,11 @@ def on_pick(event):
14671450
14681451
cid = fig.canvas.mpl_connect('pick_event', on_pick)
14691452
"""
1453+
14701454
def __init__(self, name, canvas, mouseevent, artist,
14711455
guiEvent=None, **kwargs):
1456+
if guiEvent is None:
1457+
guiEvent = mouseevent.guiEvent
14721458
super().__init__(name, canvas, guiEvent)
14731459
self.mouseevent = mouseevent
14741460
self.artist = artist
@@ -1506,10 +1492,46 @@ def on_key(event):
15061492
15071493
cid = fig.canvas.mpl_connect('key_press_event', on_key)
15081494
"""
1495+
15091496
def __init__(self, name, canvas, key, x=0, y=0, guiEvent=None):
1510-
self.key = key
1511-
# super-init deferred to the end: callback errors if called before
15121497
super().__init__(name, canvas, x, y, guiEvent=guiEvent)
1498+
self.key = key
1499+
1500+
1501+
# Default callback for key events.
1502+
def _key_handler(event):
1503+
# Dead reckoning of key.
1504+
if event.name == "key_press_event":
1505+
event.canvas._key = event.key
1506+
elif event.name == "key_release_event":
1507+
event.canvas._key = None
1508+
1509+
1510+
# Default callback for mouse events.
1511+
def _mouse_handler(event):
1512+
# Dead-reckoning of button and key.
1513+
if event.name == "button_press_event":
1514+
event.canvas._button = event.button
1515+
elif event.name == "button_release_event":
1516+
event.canvas._button = None
1517+
elif event.name == "motion_notify_event" and event.button is None:
1518+
event.button = event.canvas._button
1519+
if event.key is None:
1520+
event.key = event.canvas._key
1521+
# Emit axes_enter/axes_leave.
1522+
if event.name == "motion_notify_event":
1523+
last = LocationEvent.lastevent
1524+
last_axes = last.inaxes if last is not None else None
1525+
if last_axes != event.inaxes:
1526+
if last_axes is not None:
1527+
try:
1528+
last.canvas.callbacks.process("axes_leave_event", last)
1529+
except Exception:
1530+
pass # The last canvas may already have been torn down.
1531+
if event.inaxes is not None:
1532+
event.canvas.callbacks.process("axes_enter_event", event)
1533+
LocationEvent.lastevent = (
1534+
None if event.name == "figure_leave_event" else event)
15131535

15141536

15151537
def _get_renderer(figure, print_method=None):
@@ -1720,12 +1742,16 @@ def resize(self, w, h):
17201742
_api.warn_deprecated("3.6", name="resize", obj_type="method",
17211743
alternative="FigureManagerBase.resize")
17221744

1745+
@_api.deprecated("3.6", alternative=(
1746+
"callbacks.process('draw_event', DrawEvent(...))"))
17231747
def draw_event(self, renderer):
17241748
"""Pass a `DrawEvent` to all functions connected to ``draw_event``."""
17251749
s = 'draw_event'
17261750
event = DrawEvent(s, self, renderer)
17271751
self.callbacks.process(s, event)
17281752

1753+
@_api.deprecated("3.6", alternative=(
1754+
"callbacks.process('resize_event', ResizeEvent(...))"))
17291755
def resize_event(self):
17301756
"""
17311757
Pass a `ResizeEvent` to all functions connected to ``resize_event``.
@@ -1735,6 +1761,8 @@ def resize_event(self):
17351761
self.callbacks.process(s, event)
17361762
self.draw_idle()
17371763

1764+
@_api.deprecated("3.6", alternative=(
1765+
"callbacks.process('close_event', CloseEvent(...))"))
17381766
def close_event(self, guiEvent=None):
17391767
"""
17401768
Pass a `CloseEvent` to all functions connected to ``close_event``.
@@ -1751,6 +1779,8 @@ def close_event(self, guiEvent=None):
17511779
# AttributeError occurs on OSX with qt4agg upon exiting
17521780
# with an open window; 'callbacks' attribute no longer exists.
17531781

1782+
@_api.deprecated("3.6", alternative=(
1783+
"callbacks.process('key_press_event', KeyEvent(...))"))
17541784
def key_press_event(self, key, guiEvent=None):
17551785
"""
17561786
Pass a `KeyEvent` to all functions connected to ``key_press_event``.
@@ -1761,6 +1791,8 @@ def key_press_event(self, key, guiEvent=None):
17611791
s, self, key, self._lastx, self._lasty, guiEvent=guiEvent)
17621792
self.callbacks.process(s, event)
17631793

1794+
@_api.deprecated("3.6", alternative=(
1795+
"callbacks.process('key_release_event', KeyEvent(...))"))
17641796
def key_release_event(self, key, guiEvent=None):
17651797
"""
17661798
Pass a `KeyEvent` to all functions connected to ``key_release_event``.
@@ -1771,6 +1803,8 @@ def key_release_event(self, key, guiEvent=None):
17711803
self.callbacks.process(s, event)
17721804
self._key = None
17731805

1806+
@_api.deprecated("3.6", alternative=(
1807+
"callbacks.process('pick_event', PickEvent(...))"))
17741808
def pick_event(self, mouseevent, artist, **kwargs):
17751809
"""
17761810
Callback processing for pick events.
@@ -1787,6 +1821,8 @@ def pick_event(self, mouseevent, artist, **kwargs):
17871821
**kwargs)
17881822
self.callbacks.process(s, event)
17891823

1824+
@_api.deprecated("3.6", alternative=(
1825+
"callbacks.process('scroll_event', MouseEvent(...))"))
17901826
def scroll_event(self, x, y, step, guiEvent=None):
17911827
"""
17921828
Callback processing for scroll events.
@@ -1807,6 +1843,8 @@ def scroll_event(self, x, y, step, guiEvent=None):
18071843
step=step, guiEvent=guiEvent)
18081844
self.callbacks.process(s, mouseevent)
18091845

1846+
@_api.deprecated("3.6", alternative=(
1847+
"callbacks.process('button_press_event', MouseEvent(...))"))
18101848
def button_press_event(self, x, y, button, dblclick=False, guiEvent=None):
18111849
"""
18121850
Callback processing for mouse button press events.
@@ -1824,6 +1862,8 @@ def button_press_event(self, x, y, button, dblclick=False, guiEvent=None):
18241862
dblclick=dblclick, guiEvent=guiEvent)
18251863
self.callbacks.process(s, mouseevent)
18261864

1865+
@_api.deprecated("3.6", alternative=(
1866+
"callbacks.process('button_release_event', MouseEvent(...))"))
18271867
def button_release_event(self, x, y, button, guiEvent=None):
18281868
"""
18291869
Callback processing for mouse button release events.
@@ -1848,6 +1888,9 @@ def button_release_event(self, x, y, button, guiEvent=None):
18481888
self.callbacks.process(s, event)
18491889
self._button = None
18501890

1891+
# Also remove _lastx, _lasty when this goes away.
1892+
@_api.deprecated("3.6", alternative=(
1893+
"callbacks.process('motion_notify_event', MouseEvent(...))"))
18511894
def motion_notify_event(self, x, y, guiEvent=None):
18521895
"""
18531896
Callback processing for mouse movement events.
@@ -1873,6 +1916,8 @@ def motion_notify_event(self, x, y, guiEvent=None):
18731916
guiEvent=guiEvent)
18741917
self.callbacks.process(s, event)
18751918

1919+
@_api.deprecated("3.6", alternative=(
1920+
"callbacks.process('leave_notify_event', LocationEvent(...))"))
18761921
def leave_notify_event(self, guiEvent=None):
18771922
"""
18781923
Callback processing for the mouse cursor leaving the canvas.
@@ -1889,6 +1934,8 @@ def leave_notify_event(self, guiEvent=None):
18891934
LocationEvent.lastevent = None
18901935
self._lastx, self._lasty = None, None
18911936

1937+
@_api.deprecated("3.6", alternative=(
1938+
"callbacks.process('enter_notify_event', LocationEvent(...))"))
18921939
def enter_notify_event(self, guiEvent=None, xy=None):
18931940
"""
18941941
Callback processing for the mouse cursor entering the canvas.

0 commit comments

Comments
 (0)