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

Skip to content

Commit 427b9b6

Browse files
authored
Merge pull request #20208 from anntzer/simple-blocking-input
Rewrite blocking_input to something much simpler.
2 parents 464dcf6 + b2a7521 commit 427b9b6

File tree

5 files changed

+130
-16
lines changed

5 files changed

+130
-16
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``matplotlib.blocking_input``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
This module has been deprecated. Instead, use ``canvas.start_event_loop()``
4+
and ``canvas.stop_event_loop()`` while connecting event callbacks as needed.

lib/matplotlib/_blocking_input.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
def blocking_input_loop(figure, event_names, timeout, handler):
2+
"""
3+
Run *figure*'s event loop while listening to interactive events.
4+
5+
The events listed in *event_names* are passed to *handler*.
6+
7+
This function is used to implement `.Figure.waitforbuttonpress`,
8+
`.Figure.ginput`, and `.Axes.clabel`.
9+
10+
Parameters
11+
----------
12+
figure : `~matplotlib.figure.Figure`
13+
event_names : list of str
14+
The names of the events passed to *handler*.
15+
timeout : float
16+
If positive, the event loop is stopped after *timeout* seconds.
17+
handler : Callable[[Event], Any]
18+
Function called for each event; it can force an early exit of the event
19+
loop by calling ``canvas.stop_event_loop()``.
20+
"""
21+
if hasattr(figure.canvas, "manager"):
22+
figure.show() # Ensure that the figure is shown if we are managing it.
23+
# Connect the events to the on_event function call.
24+
cids = [figure.canvas.mpl_connect(name, handler) for name in event_names]
25+
try:
26+
figure.canvas.start_event_loop(timeout) # Start event loop.
27+
finally: # Run even on exception like ctrl-c.
28+
# Disconnect the callbacks.
29+
for cid in cids:
30+
figure.canvas.mpl_disconnect(cid)

lib/matplotlib/blocking_input.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from matplotlib.backend_bases import MouseButton
2727
import matplotlib.lines as mlines
2828

29+
_api.warn_deprecated("3.5", name=__name__, obj_type="module")
2930
_log = logging.getLogger(__name__)
3031

3132

lib/matplotlib/contour.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
Classes to support contour plotting and labelling for the Axes class.
33
"""
44

5+
import functools
56
from numbers import Integral
67

78
import numpy as np
89
from numpy import ma
910

1011
import matplotlib as mpl
11-
from matplotlib import _api
12+
from matplotlib import _api, docstring
13+
from matplotlib.backend_bases import MouseButton
1214
import matplotlib.path as mpath
1315
import matplotlib.ticker as ticker
1416
import matplotlib.cm as cm
@@ -20,9 +22,6 @@
2022
import matplotlib.patches as mpatches
2123
import matplotlib.transforms as mtransforms
2224

23-
# Import needed for adding manual selection capability to clabel
24-
from matplotlib.blocking_input import BlockingContourLabeler
25-
from matplotlib import docstring
2625

2726
# We can't use a single line collection for contour because a line
2827
# collection can have only a single line style, and we want to be able to have
@@ -45,6 +44,35 @@ def get_rotation(self):
4544
return new_angle
4645

4746

47+
def _contour_labeler_event_handler(cs, inline, inline_spacing, event):
48+
canvas = cs.axes.figure.canvas
49+
is_button = event.name == "button_press_event"
50+
is_key = event.name == "key_press_event"
51+
# Quit (even if not in infinite mode; this is consistent with
52+
# MATLAB and sometimes quite useful, but will require the user to
53+
# test how many points were actually returned before using data).
54+
if (is_button and event.button == MouseButton.MIDDLE
55+
or is_key and event.key in ["escape", "enter"]):
56+
canvas.stop_event_loop()
57+
# Pop last click.
58+
elif (is_button and event.button == MouseButton.RIGHT
59+
or is_key and event.key in ["backspace", "delete"]):
60+
# Unfortunately, if one is doing inline labels, then there is currently
61+
# no way to fix the broken contour - once humpty-dumpty is broken, he
62+
# can't be put back together. In inline mode, this does nothing.
63+
if not inline:
64+
cs.pop_label()
65+
canvas.draw()
66+
# Add new click.
67+
elif (is_button and event.button == MouseButton.LEFT
68+
# On macOS/gtk, some keys return None.
69+
or is_key and event.key is not None):
70+
if event.inaxes == cs.ax:
71+
cs.add_label_near(event.x, event.y, transform=False,
72+
inline=inline, inline_spacing=inline_spacing)
73+
canvas.draw()
74+
75+
4876
class ContourLabeler:
4977
"""Mixin to provide labelling capability to `.ContourSet`."""
5078

@@ -198,8 +226,11 @@ def clabel(self, levels=None, *,
198226
print('End manual selection with second mouse button.')
199227
if not inline:
200228
print('Remove last label by clicking third mouse button.')
201-
blocking_contour_labeler = BlockingContourLabeler(self)
202-
blocking_contour_labeler(inline, inline_spacing)
229+
mpl._blocking_input.blocking_input_loop(
230+
self.axes.figure, ["button_press_event", "key_press_event"],
231+
timeout=-1, handler=functools.partial(
232+
_contour_labeler_event_handler,
233+
self, inline, inline_spacing))
203234
else:
204235
self.labels(inline, inline_spacing)
205236

lib/matplotlib/figure.py

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import numpy as np
2323

2424
import matplotlib as mpl
25-
from matplotlib import docstring, projections
25+
from matplotlib import _blocking_input, docstring, projections
2626
from matplotlib.artist import (
2727
Artist, allow_rasterization, _finalize_rasterization)
2828
from matplotlib.backend_bases import (
@@ -33,7 +33,6 @@
3333
import matplotlib.image as mimage
3434

3535
from matplotlib.axes import Axes, SubplotBase, subplot_class_factory
36-
from matplotlib.blocking_input import BlockingMouseInput, BlockingKeyMouseInput
3736
from matplotlib.gridspec import GridSpec
3837
import matplotlib.legend as mlegend
3938
from matplotlib.patches import Rectangle
@@ -3027,12 +3026,52 @@ def ginput(self, n=1, timeout=30, show_clicks=True,
30273026
terminates input and any other key (not already used by the window
30283027
manager) selects a point.
30293028
"""
3030-
blocking_mouse_input = BlockingMouseInput(self,
3031-
mouse_add=mouse_add,
3032-
mouse_pop=mouse_pop,
3033-
mouse_stop=mouse_stop)
3034-
return blocking_mouse_input(n=n, timeout=timeout,
3035-
show_clicks=show_clicks)
3029+
clicks = []
3030+
marks = []
3031+
3032+
def handler(event):
3033+
is_button = event.name == "button_press_event"
3034+
is_key = event.name == "key_press_event"
3035+
# Quit (even if not in infinite mode; this is consistent with
3036+
# MATLAB and sometimes quite useful, but will require the user to
3037+
# test how many points were actually returned before using data).
3038+
if (is_button and event.button == mouse_stop
3039+
or is_key and event.key in ["escape", "enter"]):
3040+
self.canvas.stop_event_loop()
3041+
# Pop last click.
3042+
elif (is_button and event.button == mouse_pop
3043+
or is_key and event.key in ["backspace", "delete"]):
3044+
if clicks:
3045+
clicks.pop()
3046+
if show_clicks:
3047+
marks.pop().remove()
3048+
self.canvas.draw()
3049+
# Add new click.
3050+
elif (is_button and event.button == mouse_add
3051+
# On macOS/gtk, some keys return None.
3052+
or is_key and event.key is not None):
3053+
if event.inaxes:
3054+
clicks.append((event.xdata, event.ydata))
3055+
_log.info("input %i: %f, %f",
3056+
len(clicks), event.xdata, event.ydata)
3057+
if show_clicks:
3058+
line = mpl.lines.Line2D([event.xdata], [event.ydata],
3059+
marker="+", color="r")
3060+
event.inaxes.add_line(line)
3061+
marks.append(line)
3062+
self.canvas.draw()
3063+
if len(clicks) == n and n > 0:
3064+
self.canvas.stop_event_loop()
3065+
3066+
_blocking_input.blocking_input_loop(
3067+
self, ["button_press_event", "key_press_event"], timeout, handler)
3068+
3069+
# Cleanup.
3070+
for mark in marks:
3071+
mark.remove()
3072+
self.canvas.draw()
3073+
3074+
return clicks
30363075

30373076
def waitforbuttonpress(self, timeout=-1):
30383077
"""
@@ -3042,8 +3081,17 @@ def waitforbuttonpress(self, timeout=-1):
30423081
mouse button was pressed and None if no input was given within
30433082
*timeout* seconds. Negative values deactivate *timeout*.
30443083
"""
3045-
blocking_input = BlockingKeyMouseInput(self)
3046-
return blocking_input(timeout=timeout)
3084+
event = None
3085+
3086+
def handler(ev):
3087+
nonlocal event
3088+
event = ev
3089+
self.canvas.stop_event_loop()
3090+
3091+
_blocking_input.blocking_input_loop(
3092+
self, ["button_press_event", "key_press_event"], timeout, handler)
3093+
3094+
return None if event is None else event.name == "key_press_event"
30473095

30483096
def init_layoutgrid(self):
30493097
"""Initialize the layoutgrid for use in constrained_layout."""

0 commit comments

Comments
 (0)