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

Skip to content

Commit 1816036

Browse files
committed
Fix AnnotationBbox picking when it moves offscreen.
If it's drawn once, and then moved directly offscreen (via zoom or pan), then the offset of the box would not be updated, meaning it could be accidentally picked. It's not enough to simply update the location of the box, because children might get picked instead, so disable a pick in `contains` if the annotated point is offscreen, similar to `draw`. Fixes #16946.
1 parent c84f1b9 commit 1816036

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

lib/matplotlib/offsetbox.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,11 @@ def contains(self, mouseevent):
15231523
inside, info = self._default_contains(mouseevent)
15241524
if inside is not None:
15251525
return inside, info
1526+
1527+
xy_pixel = self._get_position_xy(None)
1528+
if not self._check_xy(None, xy_pixel):
1529+
return False, {}
1530+
15261531
t, tinfo = self.offsetbox.contains(mouseevent)
15271532
#if self.arrow_patch is not None:
15281533
# a, ainfo=self.arrow_patch.contains(event)
@@ -1641,7 +1646,6 @@ def draw(self, renderer):
16411646
return
16421647

16431648
xy_pixel = self._get_position_xy(renderer)
1644-
16451649
if not self._check_xy(renderer, xy_pixel):
16461650
return
16471651

lib/matplotlib/tests/test_offsetbox.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
from collections import namedtuple
22

3+
import numpy as np
34
from numpy.testing import assert_allclose
45
import pytest
56

67
from matplotlib.testing.decorators import image_comparison
78
import matplotlib.pyplot as plt
89
import matplotlib.patches as mpatches
910
import matplotlib.lines as mlines
11+
from matplotlib.backend_bases import MouseButton
12+
1013
from matplotlib.offsetbox import (
11-
AnchoredOffsetbox, DrawingArea, _get_packed_offsets)
14+
AnchoredOffsetbox, AnnotationBbox, DrawingArea, OffsetImage, TextArea,
15+
_get_packed_offsets)
1216

1317

1418
@image_comparison(['offsetbox_clipping'], remove_text=True)
@@ -182,3 +186,55 @@ def test_get_packed_offsets_equal(wd_list, total, sep, expected):
182186
def test_get_packed_offsets_equal_total_none_sep_none():
183187
with pytest.raises(ValueError):
184188
_get_packed_offsets([(1, 0)] * 3, total=None, sep=None, mode='equal')
189+
190+
191+
@pytest.mark.parametrize('child_type', ['draw', 'image', 'text'])
192+
@pytest.mark.parametrize('boxcoords',
193+
['axes fraction', 'axes pixels', 'axes points',
194+
'data'])
195+
def test_picking(child_type, boxcoords):
196+
# These all take up approximately the same area.
197+
if child_type == 'draw':
198+
picking_child = DrawingArea(5, 5)
199+
picking_child.add_artist(mpatches.Rectangle((0, 0), 5, 5, linewidth=0))
200+
elif child_type == 'image':
201+
im = np.ones((5, 5))
202+
im[2, 2] = 0
203+
picking_child = OffsetImage(im)
204+
elif child_type == 'text':
205+
picking_child = TextArea('\N{Black Square}', textprops={'fontsize': 5})
206+
else:
207+
assert False, f'Unknown picking child type {child_type}'
208+
209+
fig, ax = plt.subplots()
210+
ab = AnnotationBbox(picking_child, (0.5, 0.5), boxcoords=boxcoords)
211+
ab.set_picker(True)
212+
ax.add_artist(ab)
213+
214+
calls = []
215+
fig.canvas.mpl_connect('pick_event', lambda event: calls.append(event))
216+
217+
# Annotation should be picked by an event occurring at its center.
218+
if boxcoords == 'axes points':
219+
x, y = ax.transAxes.transform_point((0, 0))
220+
x += 0.5 * fig.dpi / 72
221+
y += 0.5 * fig.dpi / 72
222+
elif boxcoords == 'axes pixels':
223+
x, y = ax.transAxes.transform_point((0, 0))
224+
x += 0.5
225+
y += 0.5
226+
else:
227+
x, y = ax.transAxes.transform_point((0.5, 0.5))
228+
fig.canvas.draw()
229+
calls.clear()
230+
fig.canvas.button_press_event(x, y, MouseButton.LEFT)
231+
assert len(calls) == 1 and calls[0].artist == ab
232+
233+
# Annotation should *not* be picked by an event at its original center
234+
# point when the limits have changed enough to hide the *xy* point.
235+
ax.set_xlim(-1, 0)
236+
ax.set_ylim(-1, 0)
237+
fig.canvas.draw()
238+
calls.clear()
239+
fig.canvas.button_press_event(x, y, MouseButton.LEFT)
240+
assert len(calls) == 0

0 commit comments

Comments
 (0)