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

Skip to content

Commit c978fac

Browse files
committed
Support multi-figure MultiCursor; prepare improving its signature.
Support MultiCursor with Axes spread over different figures. As a consequence, the first parameter of MultiCursor (`canvas`) has become meaningless (requiring the user to pass in `[ax.figure.canvas for ax in axes]` seems pointless); just ignore that argument. While we're at it, also move some parameters of MultiCursor towards being keyword-only, to prepare for a hopefully better signature without the `canvas` parameter at all.
1 parent 0d39a4f commit c978fac

File tree

5 files changed

+65
-27
lines changed

5 files changed

+65
-27
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
The ``canvas`` and ``background`` attributes of ``MultiCursor``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
... are deprecated with no replacement.
4+
5+
All parameters to ``MultiCursor`` starting from *useblit*
6+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7+
... are becoming keyword-only (passing them positionally is deprecated).
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
``MultiCursor`` now supports Axes split over multiple figures
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
Previously, `.MultiCursor` only worked if all target Axes belonged to the same
4+
figure.
5+
6+
As a consequence of this change, the first argument to the `.MultiCursor`
7+
constructor has become unused (it was previously the joint canvas of all Axes,
8+
but the canvases are now directly inferred from the list of Axes).

examples/widgets/multicursor.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@
55
66
Showing a cursor on multiple plots simultaneously.
77
8-
This example generates two subplots and on hovering the cursor over data in one
9-
subplot, the values of that datapoint are shown in both respectively.
8+
This example generates three axes split over two different figures. On
9+
hovering the cursor over data in one subplot, the values of that datapoint are
10+
shown in all axes.
1011
"""
12+
1113
import numpy as np
1214
import matplotlib.pyplot as plt
1315
from matplotlib.widgets import MultiCursor
1416

1517
t = np.arange(0.0, 2.0, 0.01)
1618
s1 = np.sin(2*np.pi*t)
17-
s2 = np.sin(4*np.pi*t)
19+
s2 = np.sin(3*np.pi*t)
20+
s3 = np.sin(4*np.pi*t)
1821

1922
fig, (ax1, ax2) = plt.subplots(2, sharex=True)
2023
ax1.plot(t, s1)
2124
ax2.plot(t, s2)
25+
fig, ax3 = plt.subplots()
26+
ax3.plot(t, s3)
2227

23-
multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1)
28+
multi = MultiCursor(None, (ax1, ax2, ax3), color='r', lw=1)
2429
plt.show()
2530

2631
#############################################################################

lib/matplotlib/tests/test_widgets.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,11 +1516,12 @@ def test_polygon_selector_box(ax):
15161516
[(True, True), (True, False), (False, True)],
15171517
)
15181518
def test_MultiCursor(horizOn, vertOn):
1519-
fig, (ax1, ax2, ax3) = plt.subplots(3, sharex=True)
1519+
(ax1, ax3) = plt.figure().subplots(2, sharex=True)
1520+
ax2 = plt.figure().subplots()
15201521

15211522
# useblit=false to avoid having to draw the figure to cache the renderer
15221523
multi = widgets.MultiCursor(
1523-
fig.canvas, (ax1, ax2), useblit=False, horizOn=horizOn, vertOn=vertOn
1524+
None, (ax1, ax2), useblit=False, horizOn=horizOn, vertOn=vertOn
15241525
)
15251526

15261527
# Only two of the axes should have a line drawn on them.

lib/matplotlib/widgets.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,8 +1680,8 @@ class MultiCursor(Widget):
16801680
16811681
Parameters
16821682
----------
1683-
canvas : `matplotlib.backend_bases.FigureCanvasBase`
1684-
The FigureCanvas that contains all the Axes.
1683+
canvas : object
1684+
This parameter is entirely unused and only kept for back-compatibility.
16851685
16861686
axes : list of `matplotlib.axes.Axes`
16871687
The `~.axes.Axes` to attach the cursor to.
@@ -1708,21 +1708,29 @@ class MultiCursor(Widget):
17081708
See :doc:`/gallery/widgets/multicursor`.
17091709
"""
17101710

1711+
@_api.make_keyword_only("3.6", "useblit")
17111712
def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True,
17121713
**lineprops):
1713-
self.canvas = canvas
1714+
# canvas is stored only to provide the deprecated .canvas attribute;
1715+
# once it goes away the unused argument won't need to be stored at all.
1716+
self._canvas = canvas
1717+
17141718
self.axes = axes
17151719
self.horizOn = horizOn
17161720
self.vertOn = vertOn
17171721

1722+
self._canvas_infos = {
1723+
ax.figure.canvas: {"cids": [], "background": None} for ax in axes}
1724+
17181725
xmin, xmax = axes[-1].get_xlim()
17191726
ymin, ymax = axes[-1].get_ylim()
17201727
xmid = 0.5 * (xmin + xmax)
17211728
ymid = 0.5 * (ymin + ymax)
17221729

17231730
self.visible = True
1724-
self.useblit = useblit and self.canvas.supports_blit
1725-
self.background = None
1731+
self.useblit = (
1732+
useblit
1733+
and all(canvas.supports_blit for canvas in self._canvas_infos))
17261734
self.needclear = False
17271735

17281736
if self.useblit:
@@ -1742,33 +1750,39 @@ def __init__(self, canvas, axes, useblit=True, horizOn=False, vertOn=True,
17421750

17431751
self.connect()
17441752

1753+
canvas = _api.deprecate_privatize_attribute("3.6")
1754+
background = _api.deprecated("3.6")(lambda self: (
1755+
self._backgrounds[self.axes[0].figure.canvas] if self.axes else None))
1756+
17451757
def connect(self):
17461758
"""Connect events."""
1747-
self._cidmotion = self.canvas.mpl_connect('motion_notify_event',
1748-
self.onmove)
1749-
self._ciddraw = self.canvas.mpl_connect('draw_event', self.clear)
1759+
for canvas, info in self._canvas_infos.items():
1760+
info["cids"] = [
1761+
canvas.mpl_connect('motion_notify_event', self.onmove),
1762+
canvas.mpl_connect('draw_event', self.clear),
1763+
]
17501764

17511765
def disconnect(self):
17521766
"""Disconnect events."""
1753-
self.canvas.mpl_disconnect(self._cidmotion)
1754-
self.canvas.mpl_disconnect(self._ciddraw)
1767+
for canvas, info in self._canvas_infos.items():
1768+
for cid in info["cids"]:
1769+
canvas.mpl_disconnect(cid)
1770+
info["cids"].clear()
17551771

17561772
def clear(self, event):
17571773
"""Clear the cursor."""
17581774
if self.ignore(event):
17591775
return
17601776
if self.useblit:
1761-
self.background = (
1762-
self.canvas.copy_from_bbox(self.canvas.figure.bbox))
1777+
for canvas, info in self._canvas_infos.items():
1778+
info["background"] = canvas.copy_from_bbox(canvas.figure.bbox)
17631779
for line in self.vlines + self.hlines:
17641780
line.set_visible(False)
17651781

17661782
def onmove(self, event):
1767-
if self.ignore(event):
1768-
return
1769-
if event.inaxes not in self.axes:
1770-
return
1771-
if not self.canvas.widgetlock.available(self):
1783+
if (self.ignore(event)
1784+
or event.inaxes not in self.axes
1785+
or not event.canvas.widgetlock.available(self)):
17721786
return
17731787
self.needclear = True
17741788
if not self.visible:
@@ -1785,17 +1799,20 @@ def onmove(self, event):
17851799

17861800
def _update(self):
17871801
if self.useblit:
1788-
if self.background is not None:
1789-
self.canvas.restore_region(self.background)
1802+
for canvas, info in self._canvas_infos.items():
1803+
if info["background"]:
1804+
canvas.restore_region(info["background"])
17901805
if self.vertOn:
17911806
for ax, line in zip(self.axes, self.vlines):
17921807
ax.draw_artist(line)
17931808
if self.horizOn:
17941809
for ax, line in zip(self.axes, self.hlines):
17951810
ax.draw_artist(line)
1796-
self.canvas.blit()
1811+
for canvas in self._canvas_infos:
1812+
canvas.blit()
17971813
else:
1798-
self.canvas.draw_idle()
1814+
for canvas in self._canvas_infos:
1815+
canvas.draw_idle()
17991816

18001817

18011818
class _SelectorWidget(AxesWidget):

0 commit comments

Comments
 (0)