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

Skip to content

Commit bdcc83b

Browse files
ianhitimhoffm
andcommitted
Change styling of slider widgets
Goal is to add a clear handle for the user to grab and to provide visual feedback via color change of when the slider has been grabbed. Simplify slider background rectangles rename bkd -> track + change default color to lightgrey lint remove facecolor from snap demo example Don't change handle color on grab. correct errors in slider docstrings Co-Authored-By: Tim Hoffmann <[email protected]>
1 parent 325fac9 commit bdcc83b

File tree

4 files changed

+137
-35
lines changed

4 files changed

+137
-35
lines changed

examples/widgets/slider_demo.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,11 @@ def f(t, amplitude, frequency):
3232
line, = plt.plot(t, f(t, init_amplitude, init_frequency), lw=2)
3333
ax.set_xlabel('Time [s]')
3434

35-
axcolor = 'lightgoldenrodyellow'
36-
ax.margins(x=0)
37-
3835
# adjust the main plot to make room for the sliders
3936
plt.subplots_adjust(left=0.25, bottom=0.25)
4037

4138
# Make a horizontal slider to control the frequency.
42-
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
39+
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])
4340
freq_slider = Slider(
4441
ax=axfreq,
4542
label='Frequency [Hz]',
@@ -49,7 +46,7 @@ def f(t, amplitude, frequency):
4946
)
5047

5148
# Make a vertically oriented slider to control the amplitude
52-
axamp = plt.axes([0.1, 0.25, 0.0225, 0.63], facecolor=axcolor)
49+
axamp = plt.axes([0.1, 0.25, 0.0225, 0.63])
5350
amp_slider = Slider(
5451
ax=axamp,
5552
label="Amplitude",
@@ -72,7 +69,7 @@ def update(val):
7269

7370
# Create a `matplotlib.widgets.Button` to reset the sliders to initial values.
7471
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
75-
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
72+
button = Button(resetax, 'Reset', hovercolor='0.975')
7673

7774

7875
def reset(event):

examples/widgets/slider_snap_demo.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@
2828
plt.subplots_adjust(bottom=0.25)
2929
l, = plt.plot(t, s, lw=2)
3030

31-
slider_bkd_color = 'lightgoldenrodyellow'
32-
ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=slider_bkd_color)
33-
ax_amp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=slider_bkd_color)
31+
ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03])
32+
ax_amp = plt.axes([0.25, 0.15, 0.65, 0.03])
3433

3534
# define the values to use for snapping
3635
allowed_amplitudes = np.concatenate([np.linspace(.1, 5, 100), [6, 7, 8, 9]])
@@ -60,7 +59,7 @@ def update(val):
6059
samp.on_changed(update)
6160

6261
ax_reset = plt.axes([0.8, 0.025, 0.1, 0.04])
63-
button = Button(ax_reset, 'Reset', color=slider_bkd_color, hovercolor='0.975')
62+
button = Button(ax_reset, 'Reset', hovercolor='0.975')
6463

6564

6665
def reset(event):

lib/matplotlib/tests/test_widgets.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ def test_slider_horizontal_vertical():
286286
assert slider.val == 10
287287
# check the dimension of the slider patch in axes units
288288
box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
289-
assert_allclose(box.bounds, [0, 0, 10/24, 1])
289+
assert_allclose(box.bounds, [0, .25, 10/24, .5])
290290

291291
fig, ax = plt.subplots()
292292
slider = widgets.Slider(ax=ax, label='', valmin=0, valmax=24,
@@ -295,7 +295,7 @@ def test_slider_horizontal_vertical():
295295
assert slider.val == 10
296296
# check the dimension of the slider patch in axes units
297297
box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
298-
assert_allclose(box.bounds, [0, 0, 1, 10/24])
298+
assert_allclose(box.bounds, [.25, 0, .5, 10/24])
299299

300300

301301
@pytest.mark.parametrize("orientation", ["horizontal", "vertical"])
@@ -311,12 +311,12 @@ def test_range_slider(orientation):
311311
ax=ax, label="", valmin=0.0, valmax=1.0, orientation=orientation
312312
)
313313
box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
314-
assert_allclose(box.get_points().flatten()[idx], [0.25, 0, 0.75, 1])
314+
assert_allclose(box.get_points().flatten()[idx], [0.25, .25, 0.75, .75])
315315

316316
slider.set_val((0.2, 0.6))
317317
assert_allclose(slider.val, (0.2, 0.6))
318318
box = slider.poly.get_extents().transformed(ax.transAxes.inverted())
319-
assert_allclose(box.get_points().flatten()[idx], [0.2, 0, 0.6, 1])
319+
assert_allclose(box.get_points().flatten()[idx], [0.2, .25, 0.6, .75])
320320

321321
slider.set_val((0.2, 0.1))
322322
assert_allclose(slider.val, (0.1, 0.2))

lib/matplotlib/widgets.py

Lines changed: 127 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,9 @@ def __init__(self, ax, orientation, closedmin, closedmax,
267267
self._fmt.set_useOffset(False) # No additive offset.
268268
self._fmt.set_useMathText(True) # x sign before multiplicative offset.
269269

270-
ax.set_xticks([])
271-
ax.set_yticks([])
270+
ax.set_axis_off()
272271
ax.set_navigate(False)
272+
273273
self.connect_event("button_press_event", self._update)
274274
self.connect_event("button_release_event", self._update)
275275
if dragging:
@@ -329,7 +329,9 @@ class Slider(SliderBase):
329329
def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
330330
closedmin=True, closedmax=True, slidermin=None,
331331
slidermax=None, dragging=True, valstep=None,
332-
orientation='horizontal', *, initcolor='r', **kwargs):
332+
orientation='horizontal', *, initcolor='r',
333+
track_color='lightgrey', handle_facecolor='white',
334+
handle_edgecolor='.75', handle_size=10, **kwargs):
333335
"""
334336
Parameters
335337
----------
@@ -380,6 +382,19 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
380382
The color of the line at the *valinit* position. Set to ``'none'``
381383
for no line.
382384
385+
track_color : color, default: 'lightgrey'
386+
The color of the background track. The track is accessible for
387+
further styling via the *track* attribute.
388+
389+
handle_facecolor : color, default: 'white'
390+
The facecolor of the circular slider handle.
391+
392+
handle_edgecolor : color, default: '.75'
393+
The edgecolor of the circle slider handle.
394+
395+
handle_size : int, default: 10
396+
The size of the circular slider handle in points.
397+
383398
Notes
384399
-----
385400
Additional kwargs are passed on to ``self.poly`` which is the
@@ -404,11 +419,33 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None,
404419
self.val = valinit
405420
self.valinit = valinit
406421
if orientation == 'vertical':
407-
self.poly = ax.axhspan(valmin, valinit, 0, 1, **kwargs)
408-
self.hline = ax.axhline(valinit, 0, 1, color=initcolor, lw=1)
422+
self.track = Rectangle(
423+
(.25, 0), .5, 1,
424+
transform=ax.transAxes,
425+
facecolor=track_color
426+
)
427+
ax.add_patch(self.track)
428+
self.poly = ax.axhspan(valmin, valinit, .25, .75, **kwargs)
429+
self.hline = ax.axhline(valinit, .25, .75, color=initcolor, lw=1)
430+
handleXY = [[0.5], [valinit]]
409431
else:
410-
self.poly = ax.axvspan(valmin, valinit, 0, 1, **kwargs)
411-
self.vline = ax.axvline(valinit, 0, 1, color=initcolor, lw=1)
432+
self.track = Rectangle(
433+
(0, .25), 1, .5,
434+
transform=ax.transAxes,
435+
facecolor=track_color
436+
)
437+
ax.add_patch(self.track)
438+
self.poly = ax.axvspan(valmin, valinit, .25, .75, **kwargs)
439+
self.vline = ax.axvline(valinit, .25, .75, color=initcolor, lw=1)
440+
handleXY = [[valinit], [0.5]]
441+
self._handle, = ax.plot(
442+
*handleXY,
443+
"o",
444+
markersize=handle_size,
445+
markeredgecolor=handle_edgecolor,
446+
markerfacecolor=handle_facecolor,
447+
clip_on=False
448+
)
412449

413450
if orientation == 'vertical':
414451
self.label = ax.text(0.5, 1.02, label, transform=ax.transAxes,
@@ -499,11 +536,13 @@ def set_val(self, val):
499536
"""
500537
xy = self.poly.xy
501538
if self.orientation == 'vertical':
502-
xy[1] = 0, val
503-
xy[2] = 1, val
539+
xy[1] = .25, val
540+
xy[2] = .75, val
541+
self._handle.set_ydata([val])
504542
else:
505-
xy[2] = val, 1
506-
xy[3] = val, 0
543+
xy[2] = val, .75
544+
xy[3] = val, .25
545+
self._handle.set_xdata([val])
507546
self.poly.xy = xy
508547
self.valtext.set_text(self._format(val))
509548
if self.drawon:
@@ -558,6 +597,10 @@ def __init__(
558597
dragging=True,
559598
valstep=None,
560599
orientation="horizontal",
600+
track_color='lightgrey',
601+
handle_facecolor='white',
602+
handle_edgecolor='.75',
603+
handle_size=10,
561604
**kwargs,
562605
):
563606
"""
@@ -598,6 +641,19 @@ def __init__(
598641
orientation : {'horizontal', 'vertical'}, default: 'horizontal'
599642
The orientation of the slider.
600643
644+
track_color : color, default: 'lightgrey'
645+
The color of the background track. The track is accessible for
646+
further styling via the *track* attribute.
647+
648+
handle_facecolor : color, default: 'white'
649+
The facecolor of the circular slider handle.
650+
651+
handle_edgecolor : color, default: '.75'
652+
The edgecolor of the circular slider handles.
653+
654+
handle_size : int, default: 10
655+
The size of the circular slider handles in points.
656+
601657
Notes
602658
-----
603659
Additional kwargs are passed on to ``self.poly`` which is the
@@ -619,9 +675,43 @@ def __init__(
619675
self.val = valinit
620676
self.valinit = valinit
621677
if orientation == "vertical":
678+
self.track = Rectangle(
679+
(.25, 0), .5, 2,
680+
transform=ax.transAxes,
681+
facecolor=track_color
682+
)
683+
ax.add_patch(self.track)
622684
self.poly = ax.axhspan(valinit[0], valinit[1], 0, 1, **kwargs)
685+
handleXY_1 = [.5, valinit[0]]
686+
handleXY_2 = [.5, valinit[1]]
623687
else:
688+
self.track = Rectangle(
689+
(0, .25), 1, .5,
690+
transform=ax.transAxes,
691+
facecolor=track_color
692+
)
693+
ax.add_patch(self.track)
624694
self.poly = ax.axvspan(valinit[0], valinit[1], 0, 1, **kwargs)
695+
handleXY_1 = [valinit[0], .5]
696+
handleXY_2 = [valinit[1], .5]
697+
self._handles = [
698+
ax.plot(
699+
*handleXY_1,
700+
"o",
701+
markersize=handle_size,
702+
markeredgecolor=handle_edgecolor,
703+
markerfacecolor=handle_facecolor,
704+
clip_on=False
705+
)[0],
706+
ax.plot(
707+
*handleXY_2,
708+
"o",
709+
markersize=handle_size,
710+
markeredgecolor=handle_edgecolor,
711+
markerfacecolor=handle_facecolor,
712+
clip_on=False
713+
)[0]
714+
]
625715

626716
if orientation == "vertical":
627717
self.label = ax.text(
@@ -660,6 +750,7 @@ def __init__(
660750
horizontalalignment="left",
661751
)
662752

753+
self._active_handle = None
663754
self.set_val(valinit)
664755

665756
def _min_in_bounds(self, min):
@@ -696,6 +787,8 @@ def _update_val_from_pos(self, pos):
696787
else:
697788
val = self._max_in_bounds(pos)
698789
self.set_max(val)
790+
if self._active_handle:
791+
self._active_handle.set_xdata([val])
699792

700793
def _update(self, event):
701794
"""Update the slider position."""
@@ -714,7 +807,20 @@ def _update(self, event):
714807
):
715808
self.drag_active = False
716809
event.canvas.release_mouse(self.ax)
810+
self._active_handle = None
717811
return
812+
813+
# determine which handle was grabbed
814+
handle = self._handles[
815+
np.argmin(
816+
np.abs([h.get_xdata()[0] - event.xdata for h in self._handles])
817+
)
818+
]
819+
# these checks ensure smooth behavior if the handles swap which one
820+
# has a higher value. i.e. if one is dragged over and past the other.
821+
if handle is not self._active_handle:
822+
self._active_handle = handle
823+
718824
if self.orientation == "vertical":
719825
self._update_val_from_pos(event.ydata)
720826
else:
@@ -771,17 +877,17 @@ def set_val(self, val):
771877
val[1] = self._max_in_bounds(val[1])
772878
xy = self.poly.xy
773879
if self.orientation == "vertical":
774-
xy[0] = 0, val[0]
775-
xy[1] = 0, val[1]
776-
xy[2] = 1, val[1]
777-
xy[3] = 1, val[0]
778-
xy[4] = 0, val[0]
880+
xy[0] = .25, val[0]
881+
xy[1] = .25, val[1]
882+
xy[2] = .75, val[1]
883+
xy[3] = .75, val[0]
884+
xy[4] = .25, val[0]
779885
else:
780-
xy[0] = val[0], 0
781-
xy[1] = val[0], 1
782-
xy[2] = val[1], 1
783-
xy[3] = val[1], 0
784-
xy[4] = val[0], 0
886+
xy[0] = val[0], .25
887+
xy[1] = val[0], .75
888+
xy[2] = val[1], .75
889+
xy[3] = val[1], .25
890+
xy[4] = val[0], .25
785891
self.poly.xy = xy
786892
self.valtext.set_text(self._format(val))
787893
if self.drawon:

0 commit comments

Comments
 (0)