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

Skip to content

Commit c1861b0

Browse files
authored
Merge pull request #18569 from ianhi/slider-snap-values
More generic value snapping for Slider widgets
2 parents 1c65075 + bafcca9 commit c1861b0

15 files changed

+251
-8
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,4 @@ per-file-ignores =
287287
examples/userdemo/connectionstyle_demo.py: E402
288288
examples/userdemo/custom_boxstyle01.py: E402
289289
examples/userdemo/pgf_preamble_sgskip.py: E402
290-
examples/widgets/textbox.py: E402
290+
examples/widgets/*.py: E402
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Sliders can now snap to arbitrary values
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The `~matplotlib.widgets.Slider` UI widget now accepts arrays for *valstep*.
5+
This generalizes the previous behavior by allowing the slider to snap to
6+
arbitrary values.

examples/widgets/buttons.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,16 @@ def prev(self, event):
4848
bprev.on_clicked(callback.prev)
4949

5050
plt.show()
51+
52+
#############################################################################
53+
#
54+
# ------------
55+
#
56+
# References
57+
# """"""""""
58+
#
59+
# The use of the following functions, methods, classes and modules is shown
60+
# in this example:
61+
62+
import matplotlib
63+
matplotlib.widgets.Button

examples/widgets/check_buttons.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,16 @@ def func(label):
4141
check.on_clicked(func)
4242

4343
plt.show()
44+
45+
#############################################################################
46+
#
47+
# ------------
48+
#
49+
# References
50+
# """"""""""
51+
#
52+
# The use of the following functions, methods, classes and modules is shown
53+
# in this example:
54+
55+
import matplotlib
56+
matplotlib.widgets.CheckButtons

examples/widgets/cursor.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,16 @@
2424
cursor = Cursor(ax, useblit=True, color='red', linewidth=2)
2525

2626
plt.show()
27+
28+
#############################################################################
29+
#
30+
# ------------
31+
#
32+
# References
33+
# """"""""""
34+
#
35+
# The use of the following functions, methods, classes and modules is shown
36+
# in this example:
37+
38+
import matplotlib
39+
matplotlib.widgets.Cursor

examples/widgets/lasso_selector_demo_sgskip.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,17 @@ def accept(event):
9999
ax.set_title("Press enter to accept selected points.")
100100

101101
plt.show()
102+
103+
#############################################################################
104+
#
105+
# ------------
106+
#
107+
# References
108+
# """"""""""
109+
#
110+
# The use of the following functions, methods, classes and modules is shown
111+
# in this example:
112+
113+
import matplotlib
114+
matplotlib.widgets.LassoSelector
115+
matplotlib.path.Path

examples/widgets/multicursor.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,16 @@
2222

2323
multi = MultiCursor(fig.canvas, (ax1, ax2), color='r', lw=1)
2424
plt.show()
25+
26+
#############################################################################
27+
#
28+
# ------------
29+
#
30+
# References
31+
# """"""""""
32+
#
33+
# The use of the following functions, methods, classes and modules is shown
34+
# in this example:
35+
36+
import matplotlib
37+
matplotlib.widgets.MultiCursor

examples/widgets/polygon_selector_demo.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,17 @@ def disconnect(self):
9191
# After figure is closed print the coordinates of the selected points
9292
print('\nSelected points:')
9393
print(selector.xys[selector.ind])
94+
95+
#############################################################################
96+
#
97+
# ------------
98+
#
99+
# References
100+
# """"""""""
101+
#
102+
# The use of the following functions, methods, classes and modules is shown
103+
# in this example:
104+
105+
import matplotlib
106+
matplotlib.widgets.PolygonSelector
107+
matplotlib.path.Path

examples/widgets/radio_buttons.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,16 @@ def stylefunc(label):
5353
radio3.on_clicked(stylefunc)
5454

5555
plt.show()
56+
57+
#############################################################################
58+
#
59+
# ------------
60+
#
61+
# References
62+
# """"""""""
63+
#
64+
# The use of the following functions, methods, classes and modules is shown
65+
# in this example:
66+
67+
import matplotlib
68+
matplotlib.widgets.RadioButtons

examples/widgets/rectangle_selector.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,16 @@ def toggle_selector(event):
5757
interactive=True)
5858
fig.canvas.mpl_connect('key_press_event', toggle_selector)
5959
plt.show()
60+
61+
#############################################################################
62+
#
63+
# ------------
64+
#
65+
# References
66+
# """"""""""
67+
#
68+
# The use of the following functions, methods, classes and modules is shown
69+
# in this example:
70+
71+
import matplotlib
72+
matplotlib.widgets.RectangleSelector

examples/widgets/slider_demo.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,18 @@ def colorfunc(label):
6363
colorfunc(radio.value_selected)
6464

6565
plt.show()
66+
67+
#############################################################################
68+
#
69+
# ------------
70+
#
71+
# References
72+
# """"""""""
73+
#
74+
# The use of the following functions, methods, classes and modules is shown
75+
# in this example:
76+
77+
import matplotlib
78+
matplotlib.widgets.Button
79+
matplotlib.widgets.RadioButtons
80+
matplotlib.widgets.Slider

examples/widgets/slider_snap_demo.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
===================================
3+
Snapping Sliders to Discrete Values
4+
===================================
5+
6+
You can snap slider values to discrete values using the ``valstep`` argument.
7+
8+
In this example the Freq slider is constrained to be multiples of pi, and the
9+
Amp slider uses an array as the ``valstep`` argument to more densely sample
10+
the first part of its range.
11+
"""
12+
import numpy as np
13+
import matplotlib.pyplot as plt
14+
from matplotlib.widgets import Slider, Button
15+
16+
t = np.arange(0.0, 1.0, 0.001)
17+
a0 = 5
18+
f0 = 3
19+
s = a0 * np.sin(2 * np.pi * f0 * t)
20+
21+
fig, ax = plt.subplots()
22+
plt.subplots_adjust(bottom=0.25)
23+
l, = plt.plot(t, s, lw=2)
24+
25+
slider_bkd_color = 'lightgoldenrodyellow'
26+
ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=slider_bkd_color)
27+
ax_amp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=slider_bkd_color)
28+
29+
# define the values to use for snapping
30+
allowed_amplitudes = np.concatenate([np.linspace(.1, 5, 100), [6, 7, 8, 9]])
31+
32+
# create the sliders
33+
samp = Slider(
34+
ax_amp, "Amp", 0.1, 9.0,
35+
valinit=a0, valstep=allowed_amplitudes,
36+
color="green"
37+
)
38+
39+
sfreq = Slider(
40+
ax_freq, "Freq", 0, 10*np.pi,
41+
valinit=2*np.pi, valstep=np.pi,
42+
initcolor='none' # Remove the line marking the valinit position.
43+
)
44+
45+
46+
def update(val):
47+
amp = samp.val
48+
freq = sfreq.val
49+
l.set_ydata(amp*np.sin(2*np.pi*freq*t))
50+
fig.canvas.draw_idle()
51+
52+
53+
sfreq.on_changed(update)
54+
samp.on_changed(update)
55+
56+
ax_reset = plt.axes([0.8, 0.025, 0.1, 0.04])
57+
button = Button(ax_reset, 'Reset', color=slider_bkd_color, hovercolor='0.975')
58+
59+
60+
def reset(event):
61+
sfreq.reset()
62+
samp.reset()
63+
button.on_clicked(reset)
64+
65+
66+
plt.show()
67+
68+
#############################################################################
69+
#
70+
# ------------
71+
#
72+
# References
73+
# """"""""""
74+
#
75+
# The use of the following functions, methods, classes and modules is shown
76+
# in this example:
77+
78+
import matplotlib
79+
matplotlib.widgets.Slider
80+
matplotlib.widgets.Button

examples/widgets/span_selector.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,16 @@ def onselect(xmin, xmax):
5252

5353

5454
plt.show()
55+
56+
#############################################################################
57+
#
58+
# ------------
59+
#
60+
# References
61+
# """"""""""
62+
#
63+
# The use of the following functions, methods, classes and modules is shown
64+
# in this example:
65+
66+
import matplotlib
67+
matplotlib.widgets.SpanSelector

lib/matplotlib/tests/test_widgets.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,17 @@ def test_slider_valmin_valmax():
267267
assert slider.val == slider.valmax
268268

269269

270+
def test_slider_valstep_snapping():
271+
fig, ax = plt.subplots()
272+
slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
273+
valinit=11.4, valstep=1)
274+
assert slider.val == 11
275+
276+
slider = widgets.Slider(ax=ax, label='', valmin=0.0, valmax=24.0,
277+
valinit=11.4, valstep=[0, 1, 5.5, 19.7])
278+
assert slider.val == 5.5
279+
280+
270281
def test_slider_horizontal_vertical():
271282
fig, ax = plt.subplots()
272283
slider = widgets.Slider(ax=ax, label='', valmin=0, valmax=24,

lib/matplotlib/widgets.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from contextlib import ExitStack
1313
import copy
14-
from numbers import Integral
14+
from numbers import Integral, Number
1515

1616
import numpy as np
1717

@@ -251,7 +251,7 @@ class Slider(AxesWidget):
251251
def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
252252
closedmin=True, closedmax=True, slidermin=None,
253253
slidermax=None, dragging=True, valstep=None,
254-
orientation='horizontal', **kwargs):
254+
orientation='horizontal', *, initcolor='r', **kwargs):
255255
"""
256256
Parameters
257257
----------
@@ -291,12 +291,17 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
291291
dragging : bool, default: True
292292
If True the slider can be dragged by the mouse.
293293
294-
valstep : float, default: None
295-
If given, the slider will snap to multiples of *valstep*.
294+
valstep : float or array-like, default: None
295+
If a float, the slider will snap to multiples of *valstep*.
296+
If an array the slider will snap to the values in the array.
296297
297298
orientation : {'horizontal', 'vertical'}, default: 'horizontal'
298299
The orientation of the slider.
299300
301+
initcolor : color, default: 'r'
302+
The color of the line at the *valinit* position. Set to ``'none'``
303+
for no line.
304+
300305
Notes
301306
-----
302307
Additional kwargs are passed on to ``self.poly`` which is the
@@ -333,10 +338,10 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
333338
self.valinit = valinit
334339
if orientation == 'vertical':
335340
self.poly = ax.axhspan(valmin, valinit, 0, 1, **kwargs)
336-
self.hline = ax.axhline(valinit, 0, 1, color='r', lw=1)
341+
self.hline = ax.axhline(valinit, 0, 1, color=initcolor, lw=1)
337342
else:
338343
self.poly = ax.axvspan(valmin, valinit, 0, 1, **kwargs)
339-
self.vline = ax.axvline(valinit, 0, 1, color='r', lw=1)
344+
self.vline = ax.axvline(valinit, 0, 1, color=initcolor, lw=1)
340345

341346
if orientation == 'vertical':
342347
ax.set_ylim((valmin, valmax))
@@ -386,9 +391,16 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
386391

387392
def _value_in_bounds(self, val):
388393
"""Makes sure *val* is with given bounds."""
389-
if self.valstep:
394+
if isinstance(self.valstep, Number):
390395
val = (self.valmin
391396
+ round((val - self.valmin) / self.valstep) * self.valstep)
397+
elif self.valstep is not None:
398+
valstep = np.asanyarray(self.valstep)
399+
if valstep.ndim != 1:
400+
raise ValueError(
401+
f"valstep must have 1 dimension but has {valstep.ndim}"
402+
)
403+
val = valstep[np.argmin(np.abs(valstep - val))]
392404

393405
if val <= self.valmin:
394406
if not self.closedmin:

0 commit comments

Comments
 (0)