From 7da0279fd3b2785e8642f1ba462aa7c721b79058 Mon Sep 17 00:00:00 2001 From: ianhi Date: Sat, 9 Jan 2021 00:41:16 -0500 Subject: [PATCH 1/8] 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 <2836374+timhoffm@users.noreply.github.com> --- examples/widgets/slider_demo.py | 9 +- examples/widgets/slider_snap_demo.py | 7 +- lib/matplotlib/tests/test_widgets.py | 8 +- lib/matplotlib/widgets.py | 148 +++++++++++++++++++++++---- 4 files changed, 137 insertions(+), 35 deletions(-) diff --git a/examples/widgets/slider_demo.py b/examples/widgets/slider_demo.py index aa33315d3db4..ffe66279a77b 100644 --- a/examples/widgets/slider_demo.py +++ b/examples/widgets/slider_demo.py @@ -32,14 +32,11 @@ def f(t, amplitude, frequency): line, = plt.plot(t, f(t, init_amplitude, init_frequency), lw=2) ax.set_xlabel('Time [s]') -axcolor = 'lightgoldenrodyellow' -ax.margins(x=0) - # adjust the main plot to make room for the sliders plt.subplots_adjust(left=0.25, bottom=0.25) # Make a horizontal slider to control the frequency. -axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor) +axfreq = plt.axes([0.25, 0.1, 0.65, 0.03]) freq_slider = Slider( ax=axfreq, label='Frequency [Hz]', @@ -49,7 +46,7 @@ def f(t, amplitude, frequency): ) # Make a vertically oriented slider to control the amplitude -axamp = plt.axes([0.1, 0.25, 0.0225, 0.63], facecolor=axcolor) +axamp = plt.axes([0.1, 0.25, 0.0225, 0.63]) amp_slider = Slider( ax=axamp, label="Amplitude", @@ -72,7 +69,7 @@ def update(val): # Create a `matplotlib.widgets.Button` to reset the sliders to initial values. resetax = plt.axes([0.8, 0.025, 0.1, 0.04]) -button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975') +button = Button(resetax, 'Reset', hovercolor='0.975') def reset(event): diff --git a/examples/widgets/slider_snap_demo.py b/examples/widgets/slider_snap_demo.py index e985f84ca7ac..f02dd9a6b961 100644 --- a/examples/widgets/slider_snap_demo.py +++ b/examples/widgets/slider_snap_demo.py @@ -28,9 +28,8 @@ plt.subplots_adjust(bottom=0.25) l, = plt.plot(t, s, lw=2) -slider_bkd_color = 'lightgoldenrodyellow' -ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=slider_bkd_color) -ax_amp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=slider_bkd_color) +ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03]) +ax_amp = plt.axes([0.25, 0.15, 0.65, 0.03]) # define the values to use for snapping allowed_amplitudes = np.concatenate([np.linspace(.1, 5, 100), [6, 7, 8, 9]]) @@ -60,7 +59,7 @@ def update(val): samp.on_changed(update) ax_reset = plt.axes([0.8, 0.025, 0.1, 0.04]) -button = Button(ax_reset, 'Reset', color=slider_bkd_color, hovercolor='0.975') +button = Button(ax_reset, 'Reset', hovercolor='0.975') def reset(event): diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index f2ac7749d6ea..6784ac2ab60e 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -332,7 +332,7 @@ def test_slider_horizontal_vertical(): assert slider.val == 10 # check the dimension of the slider patch in axes units box = slider.poly.get_extents().transformed(ax.transAxes.inverted()) - assert_allclose(box.bounds, [0, 0, 10/24, 1]) + assert_allclose(box.bounds, [0, .25, 10/24, .5]) fig, ax = plt.subplots() slider = widgets.Slider(ax=ax, label='', valmin=0, valmax=24, @@ -341,7 +341,7 @@ def test_slider_horizontal_vertical(): assert slider.val == 10 # check the dimension of the slider patch in axes units box = slider.poly.get_extents().transformed(ax.transAxes.inverted()) - assert_allclose(box.bounds, [0, 0, 1, 10/24]) + assert_allclose(box.bounds, [.25, 0, .5, 10/24]) @pytest.mark.parametrize("orientation", ["horizontal", "vertical"]) @@ -358,7 +358,7 @@ def test_range_slider(orientation): valinit=[0.1, 0.34] ) box = slider.poly.get_extents().transformed(ax.transAxes.inverted()) - assert_allclose(box.get_points().flatten()[idx], [0.1, 0, 0.34, 1]) + assert_allclose(box.get_points().flatten()[idx], [0.1, 0.25, 0.34, 0.75]) # Check initial value is set correctly assert_allclose(slider.val, (0.1, 0.34)) @@ -366,7 +366,7 @@ def test_range_slider(orientation): slider.set_val((0.2, 0.6)) assert_allclose(slider.val, (0.2, 0.6)) box = slider.poly.get_extents().transformed(ax.transAxes.inverted()) - assert_allclose(box.get_points().flatten()[idx], [0.2, 0, 0.6, 1]) + assert_allclose(box.get_points().flatten()[idx], [0.2, .25, 0.6, .75]) slider.set_val((0.2, 0.1)) assert_allclose(slider.val, (0.1, 0.2)) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 7d2243f6c553..5050f73c9df0 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -267,9 +267,9 @@ def __init__(self, ax, orientation, closedmin, closedmax, self._fmt.set_useOffset(False) # No additive offset. self._fmt.set_useMathText(True) # x sign before multiplicative offset. - ax.set_xticks([]) - ax.set_yticks([]) + ax.set_axis_off() ax.set_navigate(False) + self.connect_event("button_press_event", self._update) self.connect_event("button_release_event", self._update) if dragging: @@ -329,7 +329,9 @@ class Slider(SliderBase): def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, closedmin=True, closedmax=True, slidermin=None, slidermax=None, dragging=True, valstep=None, - orientation='horizontal', *, initcolor='r', **kwargs): + orientation='horizontal', *, initcolor='r', + track_color='lightgrey', handle_facecolor='white', + handle_edgecolor='.75', handle_size=10, **kwargs): """ Parameters ---------- @@ -380,6 +382,19 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, The color of the line at the *valinit* position. Set to ``'none'`` for no line. + track_color : color, default: 'lightgrey' + The color of the background track. The track is accessible for + further styling via the *track* attribute. + + handle_facecolor : color, default: 'white' + The facecolor of the circular slider handle. + + handle_edgecolor : color, default: '.75' + The edgecolor of the circle slider handle. + + handle_size : int, default: 10 + The size of the circular slider handle in points. + Notes ----- 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, self.val = valinit self.valinit = valinit if orientation == 'vertical': - self.poly = ax.axhspan(valmin, valinit, 0, 1, **kwargs) - self.hline = ax.axhline(valinit, 0, 1, color=initcolor, lw=1) + self.track = Rectangle( + (.25, 0), .5, 1, + transform=ax.transAxes, + facecolor=track_color + ) + ax.add_patch(self.track) + self.poly = ax.axhspan(valmin, valinit, .25, .75, **kwargs) + self.hline = ax.axhline(valinit, .25, .75, color=initcolor, lw=1) + handleXY = [[0.5], [valinit]] else: - self.poly = ax.axvspan(valmin, valinit, 0, 1, **kwargs) - self.vline = ax.axvline(valinit, 0, 1, color=initcolor, lw=1) + self.track = Rectangle( + (0, .25), 1, .5, + transform=ax.transAxes, + facecolor=track_color + ) + ax.add_patch(self.track) + self.poly = ax.axvspan(valmin, valinit, .25, .75, **kwargs) + self.vline = ax.axvline(valinit, .25, .75, color=initcolor, lw=1) + handleXY = [[valinit], [0.5]] + self._handle, = ax.plot( + *handleXY, + "o", + markersize=handle_size, + markeredgecolor=handle_edgecolor, + markerfacecolor=handle_facecolor, + clip_on=False + ) if orientation == 'vertical': self.label = ax.text(0.5, 1.02, label, transform=ax.transAxes, @@ -499,11 +536,13 @@ def set_val(self, val): """ xy = self.poly.xy if self.orientation == 'vertical': - xy[1] = 0, val - xy[2] = 1, val + xy[1] = .25, val + xy[2] = .75, val + self._handle.set_ydata([val]) else: - xy[2] = val, 1 - xy[3] = val, 0 + xy[2] = val, .75 + xy[3] = val, .25 + self._handle.set_xdata([val]) self.poly.xy = xy self.valtext.set_text(self._format(val)) if self.drawon: @@ -558,6 +597,10 @@ def __init__( dragging=True, valstep=None, orientation="horizontal", + track_color='lightgrey', + handle_facecolor='white', + handle_edgecolor='.75', + handle_size=10, **kwargs, ): """ @@ -598,6 +641,19 @@ def __init__( orientation : {'horizontal', 'vertical'}, default: 'horizontal' The orientation of the slider. + track_color : color, default: 'lightgrey' + The color of the background track. The track is accessible for + further styling via the *track* attribute. + + handle_facecolor : color, default: 'white' + The facecolor of the circular slider handle. + + handle_edgecolor : color, default: '.75' + The edgecolor of the circular slider handles. + + handle_size : int, default: 10 + The size of the circular slider handles in points. + Notes ----- Additional kwargs are passed on to ``self.poly`` which is the @@ -620,9 +676,43 @@ def __init__( self.val = valinit self.valinit = valinit if orientation == "vertical": + self.track = Rectangle( + (.25, 0), .5, 2, + transform=ax.transAxes, + facecolor=track_color + ) + ax.add_patch(self.track) self.poly = ax.axhspan(valinit[0], valinit[1], 0, 1, **kwargs) + handleXY_1 = [.5, valinit[0]] + handleXY_2 = [.5, valinit[1]] else: + self.track = Rectangle( + (0, .25), 1, .5, + transform=ax.transAxes, + facecolor=track_color + ) + ax.add_patch(self.track) self.poly = ax.axvspan(valinit[0], valinit[1], 0, 1, **kwargs) + handleXY_1 = [valinit[0], .5] + handleXY_2 = [valinit[1], .5] + self._handles = [ + ax.plot( + *handleXY_1, + "o", + markersize=handle_size, + markeredgecolor=handle_edgecolor, + markerfacecolor=handle_facecolor, + clip_on=False + )[0], + ax.plot( + *handleXY_2, + "o", + markersize=handle_size, + markeredgecolor=handle_edgecolor, + markerfacecolor=handle_facecolor, + clip_on=False + )[0] + ] if orientation == "vertical": self.label = ax.text( @@ -661,6 +751,7 @@ def __init__( horizontalalignment="left", ) + self._active_handle = None self.set_val(valinit) def _min_in_bounds(self, min): @@ -698,6 +789,8 @@ def _update_val_from_pos(self, pos): else: val = self._max_in_bounds(pos) self.set_max(val) + if self._active_handle: + self._active_handle.set_xdata([val]) def _update(self, event): """Update the slider position.""" @@ -716,7 +809,20 @@ def _update(self, event): ): self.drag_active = False event.canvas.release_mouse(self.ax) + self._active_handle = None return + + # determine which handle was grabbed + handle = self._handles[ + np.argmin( + np.abs([h.get_xdata()[0] - event.xdata for h in self._handles]) + ) + ] + # these checks ensure smooth behavior if the handles swap which one + # has a higher value. i.e. if one is dragged over and past the other. + if handle is not self._active_handle: + self._active_handle = handle + if self.orientation == "vertical": self._update_val_from_pos(event.ydata) else: @@ -773,17 +879,17 @@ def set_val(self, val): val[1] = self._max_in_bounds(val[1]) xy = self.poly.xy if self.orientation == "vertical": - xy[0] = 0, val[0] - xy[1] = 0, val[1] - xy[2] = 1, val[1] - xy[3] = 1, val[0] - xy[4] = 0, val[0] + xy[0] = .25, val[0] + xy[1] = .25, val[1] + xy[2] = .75, val[1] + xy[3] = .75, val[0] + xy[4] = .25, val[0] else: - xy[0] = val[0], 0 - xy[1] = val[0], 1 - xy[2] = val[1], 1 - xy[3] = val[1], 0 - xy[4] = val[0], 0 + xy[0] = val[0], .25 + xy[1] = val[0], .75 + xy[2] = val[1], .75 + xy[3] = val[1], .25 + xy[4] = val[0], .25 self.poly.xy = xy self.valtext.set_text(self._format(val)) if self.drawon: From 61304ad1216d3fb5b59dae94dda1f6b4e826b235 Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Tue, 12 Jan 2021 17:49:44 -0500 Subject: [PATCH 2/8] correctly specify the type of the slider `poly` attribute --- lib/matplotlib/widgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 5050f73c9df0..1a8d3de88798 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -398,8 +398,8 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, Notes ----- Additional kwargs are passed on to ``self.poly`` which is the - `~matplotlib.patches.Rectangle` that draws the slider knob. See the - `.Rectangle` documentation for valid property names (``facecolor``, + `~matplotlib.patches.Polygon` that draws the slider knob. See the + `.Polygon` documentation for valid property names (``facecolor``, ``edgecolor``, ``alpha``, etc.). """ super().__init__(ax, orientation, closedmin, closedmax, @@ -657,8 +657,8 @@ def __init__( Notes ----- Additional kwargs are passed on to ``self.poly`` which is the - `~matplotlib.patches.Rectangle` that draws the slider knob. See the - `.Rectangle` documentation for valid property names (``facecolor``, + `~matplotlib.patches.Polygon` that draws the slider knob. See the + `.Polygon` documentation for valid property names (``facecolor``, ``edgecolor``, ``alpha``, etc.). """ super().__init__(ax, orientation, closedmin, closedmax, From 776af75fde690e05a3faece29a32f92de4e332ee Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Fri, 22 Jan 2021 23:41:19 -0500 Subject: [PATCH 3/8] Consolidate slider handle styling kwargs Co-Authored-By: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/widgets.py | 69 +++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 1a8d3de88798..c8d26b6b653b 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -330,8 +330,7 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, closedmin=True, closedmax=True, slidermin=None, slidermax=None, dragging=True, valstep=None, orientation='horizontal', *, initcolor='r', - track_color='lightgrey', handle_facecolor='white', - handle_edgecolor='.75', handle_size=10, **kwargs): + track_color='lightgrey', handle_style=None, **kwargs): """ Parameters ---------- @@ -386,14 +385,16 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, The color of the background track. The track is accessible for further styling via the *track* attribute. - handle_facecolor : color, default: 'white' - The facecolor of the circular slider handle. + handle_style : dict + Properties of the slider handle. Supported values are - handle_edgecolor : color, default: '.75' - The edgecolor of the circle slider handle. - - handle_size : int, default: 10 - The size of the circular slider handle in points. + ========= ===== ======= ======================================== + Key Value Default Description + ========= ===== ======= ======================================== + facecolor color 'white' The facecolor of the slider handle. + edgecolor color '.75' The edgecolor of the slider handle. + size int 10 The size of the slider handle in points. + ========= ===== ======= ======================================== Notes ----- @@ -418,6 +419,15 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, valinit = valmin self.val = valinit self.valinit = valinit + + marker_props = {} + defaults = {'facecolor':'white', 'edgecolor':'.75', 'size':10} + if handle_style is not None: + for k in ['facecolor', 'edgecolor', 'size']: + marker_props[f'marker{k}'] = handle_style.get(k, defaults[k]) + else: + marker_props = {f'marker{k}': v for k, v in defaults.items()} + if orientation == 'vertical': self.track = Rectangle( (.25, 0), .5, 1, @@ -441,9 +451,7 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, self._handle, = ax.plot( *handleXY, "o", - markersize=handle_size, - markeredgecolor=handle_edgecolor, - markerfacecolor=handle_facecolor, + **marker_props, clip_on=False ) @@ -598,9 +606,7 @@ def __init__( valstep=None, orientation="horizontal", track_color='lightgrey', - handle_facecolor='white', - handle_edgecolor='.75', - handle_size=10, + handle_style=None, **kwargs, ): """ @@ -645,14 +651,16 @@ def __init__( The color of the background track. The track is accessible for further styling via the *track* attribute. - handle_facecolor : color, default: 'white' - The facecolor of the circular slider handle. + handle_style : dict + Properties of the slider handles. Supported values are - handle_edgecolor : color, default: '.75' - The edgecolor of the circular slider handles. - - handle_size : int, default: 10 - The size of the circular slider handles in points. + ========= ===== ======= ========================================= + Key Value Default Description + ========= ===== ======= ========================================= + facecolor color 'white' The facecolor of the slider handles. + edgecolor color '.75' The edgecolor of the slider handles. + size int 10 The size of the slider handles in points. + ========= ===== ======= ========================================= Notes ----- @@ -675,6 +683,15 @@ def __init__( valinit = self._value_in_bounds(valinit) self.val = valinit self.valinit = valinit + + marker_props = {} + defaults = {'facecolor':'white', 'edgecolor':'.75', 'size':10} + if handle_style is not None: + for k in ['facecolor', 'edgecolor', 'size']: + marker_props[f'marker{k}'] = handle_style.get(k, defaults[k]) + else: + marker_props = {f'marker{k}': v for k, v in defaults.items()} + if orientation == "vertical": self.track = Rectangle( (.25, 0), .5, 2, @@ -699,17 +716,13 @@ def __init__( ax.plot( *handleXY_1, "o", - markersize=handle_size, - markeredgecolor=handle_edgecolor, - markerfacecolor=handle_facecolor, + **marker_props, clip_on=False )[0], ax.plot( *handleXY_2, "o", - markersize=handle_size, - markeredgecolor=handle_edgecolor, - markerfacecolor=handle_facecolor, + **marker_props, clip_on=False )[0] ] From 2f153b84cc510818943e574a380b17f6dc9f9584 Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Mon, 15 Feb 2021 19:20:13 -0500 Subject: [PATCH 4/8] promote all keys to marker{key} Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/widgets.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index c8d26b6b653b..275c4c01fec4 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -420,13 +420,8 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, self.val = valinit self.valinit = valinit - marker_props = {} defaults = {'facecolor':'white', 'edgecolor':'.75', 'size':10} - if handle_style is not None: - for k in ['facecolor', 'edgecolor', 'size']: - marker_props[f'marker{k}'] = handle_style.get(k, defaults[k]) - else: - marker_props = {f'marker{k}': v for k, v in defaults.items()} + marker_props = {f'marker{k}': v for k, v in {**defaults, **handle_style}} if orientation == 'vertical': self.track = Rectangle( From 1e3f6526d0f4868affe2a53cac7fb94d1acd0778 Mon Sep 17 00:00:00 2001 From: ianhi Date: Mon, 15 Feb 2021 19:26:47 -0500 Subject: [PATCH 5/8] expand doc string for handle_style --- lib/matplotlib/widgets.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 275c4c01fec4..6c909533bd52 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -386,7 +386,7 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, further styling via the *track* attribute. handle_style : dict - Properties of the slider handle. Supported values are + Properties of the slider handle. Default values are ========= ===== ======= ======================================== Key Value Default Description @@ -396,6 +396,10 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, size int 10 The size of the slider handle in points. ========= ===== ======= ======================================== + Other values will be transformed as marker{foo} and passed to the + `~.Line2D` constructor. e.g. ``handle_style = {'style'='x'}`` will + result in ``markerstyle = 'x'``. + Notes ----- Additional kwargs are passed on to ``self.poly`` which is the @@ -420,8 +424,10 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, self.val = valinit self.valinit = valinit - defaults = {'facecolor':'white', 'edgecolor':'.75', 'size':10} - marker_props = {f'marker{k}': v for k, v in {**defaults, **handle_style}} + defaults = {'facecolor': 'white', 'edgecolor': '.75', 'size': 10} + marker_props = { + f'marker{k}': v for k, v in {**defaults, **handle_style} + } if orientation == 'vertical': self.track = Rectangle( @@ -647,7 +653,7 @@ def __init__( further styling via the *track* attribute. handle_style : dict - Properties of the slider handles. Supported values are + Properties of the slider handles. Default values are ========= ===== ======= ========================================= Key Value Default Description @@ -657,6 +663,10 @@ def __init__( size int 10 The size of the slider handles in points. ========= ===== ======= ========================================= + Other values will be transformed as marker{foo} and passed to the + `~.Line2D` constructor. e.g. ``handle_style = {'style'='x'}`` will + result in ``markerstyle = 'x'``. + Notes ----- Additional kwargs are passed on to ``self.poly`` which is the @@ -679,13 +689,10 @@ def __init__( self.val = valinit self.valinit = valinit - marker_props = {} - defaults = {'facecolor':'white', 'edgecolor':'.75', 'size':10} - if handle_style is not None: - for k in ['facecolor', 'edgecolor', 'size']: - marker_props[f'marker{k}'] = handle_style.get(k, defaults[k]) - else: - marker_props = {f'marker{k}': v for k, v in defaults.items()} + defaults = {'facecolor': 'white', 'edgecolor': '.75', 'size': 10} + marker_props = { + f'marker{k}': v for k, v in {**defaults, **handle_style} + } if orientation == "vertical": self.track = Rectangle( From e160db4d3b58f124f26711bed68c06a30a676084 Mon Sep 17 00:00:00 2001 From: ianhi Date: Mon, 15 Feb 2021 20:24:00 -0500 Subject: [PATCH 6/8] fix errors in processing handle style --- lib/matplotlib/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 6c909533bd52..1d4350f5b24c 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -425,8 +425,9 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, self.valinit = valinit defaults = {'facecolor': 'white', 'edgecolor': '.75', 'size': 10} + handle_style = {} if handle_style is None else handle_style marker_props = { - f'marker{k}': v for k, v in {**defaults, **handle_style} + f'marker{k}': v for k, v in {**defaults, **handle_style}.items() } if orientation == 'vertical': @@ -690,8 +691,9 @@ def __init__( self.valinit = valinit defaults = {'facecolor': 'white', 'edgecolor': '.75', 'size': 10} + handle_style = {} if handle_style is None else handle_style marker_props = { - f'marker{k}': v for k, v in {**defaults, **handle_style} + f'marker{k}': v for k, v in {**defaults, **handle_style}.items() } if orientation == "vertical": From a7c4df6a4304cf71514858cd5f998e66588d8b7c Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Sun, 7 Mar 2021 22:38:44 -0500 Subject: [PATCH 7/8] tune slider initline parameters to minimize asymmetry Co-Authored-By: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> --- lib/matplotlib/widgets.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 1d4350f5b24c..09573e45950e 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -438,7 +438,7 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, ) ax.add_patch(self.track) self.poly = ax.axhspan(valmin, valinit, .25, .75, **kwargs) - self.hline = ax.axhline(valinit, .25, .75, color=initcolor, lw=1) + self.hline = ax.axhline(valinit, .15, .85, color=initcolor, lw=1) handleXY = [[0.5], [valinit]] else: self.track = Rectangle( @@ -448,7 +448,12 @@ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt=None, ) ax.add_patch(self.track) self.poly = ax.axvspan(valmin, valinit, .25, .75, **kwargs) - self.vline = ax.axvline(valinit, .25, .75, color=initcolor, lw=1) + # These asymmetric limits (.2, .9) minimize the asymmetry + # above and below the *poly* when rendered to pixels. + # This seems to be different for Horizontal and Vertical lines. + # For discussion see: + # https://github.com/matplotlib/matplotlib/pull/19265 + self.vline = ax.axvline(valinit, .2, .9, color=initcolor, lw=1) handleXY = [[valinit], [0.5]] self._handle, = ax.plot( *handleXY, From f5dcedd2e989fac1fbc7ab54d313f8698c5717c9 Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Wed, 23 Jun 2021 17:17:38 -0400 Subject: [PATCH 8/8] Add whats new for new slider styles --- doc/users/next_whats_new/slider_styling.rst | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 doc/users/next_whats_new/slider_styling.rst diff --git a/doc/users/next_whats_new/slider_styling.rst b/doc/users/next_whats_new/slider_styling.rst new file mode 100644 index 000000000000..f007954b4806 --- /dev/null +++ b/doc/users/next_whats_new/slider_styling.rst @@ -0,0 +1,42 @@ +Updated the appearance of Slider widgets +---------------------------------------- + +The appearance of `~.Slider` and `~.RangeSlider` widgets +were updated and given new styling parameters for the +added handles. + +.. plot:: + + import matplotlib.pyplot as plt + from matplotlib.widgets import Slider + + plt.figure(figsize=(4, 2)) + ax_old = plt.axes([0.2, 0.65, 0.65, 0.1]) + ax_new = plt.axes([0.2, 0.25, 0.65, 0.1]) + Slider(ax_new, "New", 0, 1) + + ax = ax_old + valmin = 0 + valinit = 0.5 + ax.set_xlim([0, 1]) + ax_old.axvspan(valmin, valinit, 0, 1) + ax.axvline(valinit, 0, 1, color="r", lw=1) + ax.set_xticks([]) + ax.set_yticks([]) + ax.text( + -0.02, + 0.5, + "Old", + transform=ax.transAxes, + verticalalignment="center", + horizontalalignment="right", + ) + + ax.text( + 1.02, + 0.5, + "0.5", + transform=ax.transAxes, + verticalalignment="center", + horizontalalignment="left", + )