Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 33a0599

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

File tree

Expand file treeCollapse file tree

15 files changed

+418
-327
lines changed
Filter options
Expand file treeCollapse file tree

15 files changed

+418
-327
lines changed
+11Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/artist.py
+3-1Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_bases.py
+83-36Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.