Skip to content

Commit a7b7260

Browse files
authored
Merge pull request #21374 from ericpre/snap_selectors
Snap selectors
2 parents 036f103 + 99f11d5 commit a7b7260

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SpanSelector widget can now be snapped to specified values
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
The SpanSelector widget can now be snapped to values specified by the *snap_values*
4+
argument.

lib/matplotlib/tests/test_widgets.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ def test_rectangle_selector():
6767
@pytest.mark.parametrize('spancoords', ['data', 'pixels'])
6868
@pytest.mark.parametrize('minspanx, x1', [[0, 10], [1, 10.5], [1, 11]])
6969
@pytest.mark.parametrize('minspany, y1', [[0, 10], [1, 10.5], [1, 11]])
70-
def test_rectangle_minspan(spancoords, minspanx, x1, minspany, y1):
71-
ax = get_ax()
70+
def test_rectangle_minspan(ax, spancoords, minspanx, x1, minspany, y1):
7271
# attribute to track number of onselect calls
7372
ax._n_onselect = 0
7473

@@ -924,6 +923,37 @@ def mean(vmin, vmax):
924923
assert ln2.stale is False
925924

926925

926+
def test_snapping_values_span_selector(ax):
927+
def onselect(*args):
928+
pass
929+
930+
tool = widgets.SpanSelector(ax, onselect, direction='horizontal',)
931+
snap_function = tool._snap
932+
933+
snap_values = np.linspace(0, 5, 11)
934+
values = np.array([-0.1, 0.1, 0.2, 0.5, 0.6, 0.7, 0.9, 4.76, 5.0, 5.5])
935+
expect = np.array([00.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 5.00, 5.0, 5.0])
936+
values = snap_function(values, snap_values)
937+
assert_allclose(values, expect)
938+
939+
940+
def test_span_selector_snap(ax):
941+
def onselect(vmin, vmax):
942+
ax._got_onselect = True
943+
944+
snap_values = np.arange(50) * 4
945+
946+
tool = widgets.SpanSelector(ax, onselect, direction='horizontal',
947+
snap_values=snap_values)
948+
tool.extents = (17, 35)
949+
assert tool.extents == (16, 36)
950+
951+
tool.snap_values = None
952+
assert tool.snap_values is None
953+
tool.extents = (17, 35)
954+
assert tool.extents == (17, 35)
955+
956+
927957
def check_lasso_selector(**kwargs):
928958
ax = get_ax()
929959

lib/matplotlib/widgets.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2238,6 +2238,9 @@ def on_select(min: float, max: float) -> Any
22382238
If `True`, the event triggered outside the span selector will be
22392239
ignored.
22402240
2241+
snap_values : 1D array-like, optional
2242+
Snap the selector edges to the given values.
2243+
22412244
Examples
22422245
--------
22432246
>>> import matplotlib.pyplot as plt
@@ -2259,7 +2262,7 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
22592262
props=None, onmove_callback=None, interactive=False,
22602263
button=None, handle_props=None, grab_range=10,
22612264
state_modifier_keys=None, drag_from_anywhere=False,
2262-
ignore_event_outside=False):
2265+
ignore_event_outside=False, snap_values=None):
22632266

22642267
if state_modifier_keys is None:
22652268
state_modifier_keys = dict(clear='escape',
@@ -2278,6 +2281,7 @@ def __init__(self, ax, onselect, direction, minspan=0, useblit=False,
22782281

22792282
self.visible = True
22802283
self._extents_on_press = None
2284+
self.snap_values = snap_values
22812285

22822286
# self._pressv is deprecated and we don't use it internally anymore
22832287
# but we maintain it until it is removed
@@ -2577,6 +2581,15 @@ def _contains(self, event):
25772581
"""Return True if event is within the patch."""
25782582
return self._selection_artist.contains(event, radius=0)[0]
25792583

2584+
@staticmethod
2585+
def _snap(values, snap_values):
2586+
"""Snap values to a given array values (snap_values)."""
2587+
# take into account machine precision
2588+
eps = np.min(np.abs(np.diff(snap_values))) * 1e-12
2589+
return tuple(
2590+
snap_values[np.abs(snap_values - v + np.sign(v) * eps).argmin()]
2591+
for v in values)
2592+
25802593
@property
25812594
def extents(self):
25822595
"""Return extents of the span selector."""
@@ -2591,6 +2604,8 @@ def extents(self):
25912604
@extents.setter
25922605
def extents(self, extents):
25932606
# Update displayed shape
2607+
if self.snap_values is not None:
2608+
extents = tuple(self._snap(extents, self.snap_values))
25942609
self._draw_shape(*extents)
25952610
if self._interactive:
25962611
# Update displayed handles

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy