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

Skip to content

Commit 7d40bc8

Browse files
committed
axes_grid1: add host/parasite axes pick methods
Fixes #5581
1 parent f7b88be commit 7d40bc8

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

lib/mpl_toolkits/axes_grid1/parasite_axes.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,36 @@ def cla(self):
3636
self.xaxis.set_zorder(2.5)
3737
self.yaxis.set_zorder(2.5)
3838

39+
# Overwritten so events that actually happened in the host axes still count
40+
def pick(self, mouseevent):
41+
"""
42+
call signature::
43+
44+
pick(mouseevent)
45+
46+
each child artist will fire a pick event if *mouseevent* is over
47+
the artist and the artist has picker set
48+
"""
49+
# Pick self
50+
if self.pickable():
51+
picker = self.get_picker()
52+
if callable(picker):
53+
inside, prop = picker(self, mouseevent)
54+
else:
55+
inside, prop = self.contains(mouseevent)
56+
if inside:
57+
self.figure.canvas.pick_event(mouseevent, self, **prop)
58+
59+
# Pick children
60+
for a in self.get_children():
61+
# make sure the event happened in the same axes (never happens but
62+
# whatever) or in host axes
63+
ax = getattr(a, 'axes', None)
64+
if mouseevent.inaxes is None or ax is None or \
65+
mouseevent.inaxes == ax or \
66+
(hasattr(mouseevent.inaxes, "parasites") and
67+
self in mouseevent.inaxes.parasites):
68+
a.pick(mouseevent)
3969

4070
@functools.lru_cache(None)
4171
def parasite_axes_class_factory(axes_class=None):
@@ -232,6 +262,45 @@ def cla(self):
232262
ax.cla()
233263
super().cla()
234264

265+
# Need to overwrite this so children of parasite axes get picked as well
266+
def pick(self, mouseevent):
267+
"""
268+
call signature::
269+
270+
pick(mouseevent)
271+
272+
each child artist will fire a pick event if *mouseevent* is over
273+
the artist and the artist has picker set
274+
"""
275+
# Pick self
276+
if self.pickable():
277+
picker = self.get_picker()
278+
if callable(picker):
279+
inside, prop = picker(self, mouseevent)
280+
else:
281+
inside, prop = self.contains(mouseevent)
282+
if inside:
283+
self.figure.canvas.pick_event(mouseevent, self, **prop)
284+
285+
# Pick children
286+
for a in self.get_children():
287+
# make sure the event happened in the same axes
288+
ax = getattr(a, 'axes', None)
289+
if mouseevent.inaxes is None or ax is None or \
290+
mouseevent.inaxes == ax:
291+
# we need to check if mouseevent.inaxes is None
292+
# because some objects associated with an axes (e.g., a
293+
# tick label) can be outside the bounding box of the
294+
# axes and inaxes will be None
295+
# also check that ax is None so that it traverse objects
296+
# which do no have an axes property but children might
297+
a.pick(mouseevent)
298+
299+
# Pick parasite axes
300+
for a in self.parasites:
301+
a.pick(mouseevent)
302+
303+
235304
def twinx(self, axes_class=None):
236305
"""
237306
create a twin of Axes for generating a plot with a sharex

lib/mpl_toolkits/tests/test_axes_grid1.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@
1717
AnchoredSizeBar,
1818
AnchoredDirectionArrows)
1919

20+
from matplotlib.backend_bases import MouseEvent
2021
from matplotlib.colors import LogNorm
2122
from matplotlib.transforms import Bbox, TransformedBbox
2223
from itertools import product
24+
import queue
2325

2426
import pytest
2527
import platform
2628

2729
import numpy as np
2830
from numpy.testing import assert_array_equal, assert_array_almost_equal
2931

32+
import pytest
33+
3034

3135
@image_comparison(baseline_images=['divider_append_axes'])
3236
def test_divider_append_axes():
@@ -422,3 +426,69 @@ def test_gettightbbox():
422426
bbox = fig.get_tightbbox(fig.canvas.get_renderer())
423427
np.testing.assert_array_almost_equal(bbox.extents,
424428
[-17.7, -13.9, 7.2, 5.4])
429+
430+
431+
class TestPickingCallbacksOverlap(object):
432+
"""Test pick events on normal, host or parasite axes."""
433+
# Two rectangles are drawn and "clicked on", a small one and a big one
434+
# enclosing the small one. The axis on which they are drawn as well as the
435+
# rectangle that is clicked on are varied.
436+
# In each case we expect that both rectangles are picked if we click on the
437+
# small one and only the big one is picked if we click on the big one.
438+
# Also tests picking on normal axes ("gca") as a control.
439+
@pytest.fixture(autouse=True)
440+
def setup(self):
441+
self.q = queue.Queue()
442+
self.big = plt.Rectangle((0.25, 0.25), 0.5, 0.5, picker=5)
443+
self.small = plt.Rectangle((0.4, 0.4), 0.2, 0.2, facecolor="r",
444+
picker=5)
445+
plt.gcf().canvas.mpl_connect('pick_event', self.on_pick)
446+
447+
def on_pick(self, event):
448+
self.q.put(event)
449+
450+
@pytest.mark.parametrize("click_on", [ "big", "small" ])
451+
@pytest.mark.parametrize("big_on_axes,small_on_axes", [
452+
("gca", "gca"),
453+
("host", "host"),
454+
("host", "parasite"),
455+
("parasite", "host"),
456+
("parasite", "parasite")
457+
])
458+
def test_picking_simple(self, big_on_axes, small_on_axes, click_on):
459+
# Shortcut
460+
rectangles_on_axes = (big_on_axes, small_on_axes)
461+
# Axes setup
462+
axes = { "gca": None, "host": None, "parasite": None }
463+
if "gca" in rectangles_on_axes:
464+
axes["gca"] = plt.gca()
465+
if "host" in rectangles_on_axes or "parasite" in rectangles_on_axes:
466+
axes["host"] = host_subplot(111)
467+
axes["parasite"] = axes["host"].twin()
468+
# Add rectangles to axes
469+
axes[big_on_axes].add_patch(self.big)
470+
axes[small_on_axes].add_patch(self.small)
471+
# Simulate picking with click mouse event
472+
if click_on == "big":
473+
click_axes = axes[big_on_axes]
474+
axes_coords = (0.3, 0.3)
475+
else:
476+
click_axes = axes[small_on_axes]
477+
axes_coords = (0.5, 0.5)
478+
# In reality mouse events never happen on parasite axes, only host axes
479+
if click_axes is axes["parasite"]:
480+
click_axes = axes["host"]
481+
(x, y) = click_axes.transAxes.transform(axes_coords)
482+
m = MouseEvent("button_press_event", click_axes.figure.canvas, x, y,
483+
button=1)
484+
click_axes.pick(m)
485+
# Wait at most a second for events; actual waiting only happens if sth.
486+
# is wrong and tests fail, so this won't slow down normal testing
487+
n_events = 2 if click_on == "small" else 1
488+
event_rects = []
489+
for i in range(n_events):
490+
event = self.q.get(True, 0.5)
491+
event_rects.append(event.artist)
492+
assert self.big in event_rects
493+
if click_on == "small":
494+
assert self.small in event_rects

0 commit comments

Comments
 (0)