Añadir un control deslizante vertical con matplotlib

Me gustaría crear un widget de control deslizante vertical en lugar de un control deslizante horizontal con matplotlib.

He encontrado un buen ejemplo en la página web matplotlib http://matplotlib.org/examples/widgets/slider_demo.html pero no sé cómo mover el control deslizante en el eje Y y cambiar las tags del control deslizante. Puedo cambiar la posición del eje pero no el movimiento del deslizador. El ejemplo está aquí:

import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import Slider, Button, RadioButtons fig, ax = plt.subplots() plt.subplots_adjust(left=0.25, bottom=0.25) t = np.arange(0.0, 1.0, 0.001) a0 = 5 f0 = 3 s = a0*np.sin(2*np.pi*f0*t) l, = plt.plot(t,s, lw=2, color='red') plt.axis([0, 1, -10, 10]) axcolor = 'lightgoldenrodyellow' axfreq = plt.axes([0.03, 0.25, 0.03, 0.65], axisbg=axcolor) axamp = plt.axes([0.08, 0.25, 0.03, 0.65], axisbg=axcolor) sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0) samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0) def update(val): amp = samp.val freq = sfreq.val l.set_ydata(amp*np.sin(2*np.pi*freq*t)) fig.canvas.draw_idle() sfreq.on_changed(update) samp.on_changed(update) resetax = plt.axes([0.8, 0.025, 0.1, 0.04]) button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975') def reset(event): sfreq.reset() samp.reset() button.on_clicked(reset) rax = plt.axes([0.5, 0.025, 0.15, 0.15], axisbg=axcolor) radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0) def colorfunc(label): l.set_color(label) fig.canvas.draw_idle() radio.on_clicked(colorfunc) plt.show() 

Esto no es posible fuera de la caja, porque la implementación de matplotlib.widgets.Slider utiliza axvspan y axvline para definir la barra deslizante (que es un patches.Polygon ), y la actualiza de acuerdo con el supuesto horizontal. No es demasiado difícil escribir su propio control deslizante vertical utilizando el control deslizante horizontal como ejemplo (también sería una subclase de AxesWidget ), pero debe hacerlo usted mismo.

Válido a partir de matplotlib 2.0: A continuación se ofrece una clase de control deslizante vertical; Funciona igual que la horizontal, excepto que es … bueno … ¡vertical!

 from matplotlib.widgets import AxesWidget import six class VertSlider(AxesWidget): """ A slider representing a floating point range. For the slider to remain responsive you must maintain a reference to it. The following attributes are defined *ax* : the slider :class:`matplotlib.axes.Axes` instance *val* : the current slider value *hline* : a :class:`matplotlib.lines.Line2D` instance representing the initial value of the slider *poly* : A :class:`matplotlib.patches.Polygon` instance which is the slider knob *valfmt* : the format string for formatting the slider text *label* : a :class:`matplotlib.text.Text` instance for the slider label *closedmin* : whether the slider is closed on the minimum *closedmax* : whether the slider is closed on the maximum *slidermin* : another slider - if not *None*, this slider must be greater than *slidermin* *slidermax* : another slider - if not *None*, this slider must be less than *slidermax* *dragging* : allow for mouse dragging on slider Call :meth:`on_changed` to connect to the slider event """ def __init__(self, ax, label, valmin, valmax, valinit=0.5, valfmt='%1.2f', closedmin=True, closedmax=True, slidermin=None, slidermax=None, dragging=True, **kwargs): """ Create a slider from *valmin* to *valmax* in axes *ax*. Additional kwargs are passed on to ``self.poly`` which is the :class:`matplotlib.patches.Rectangle` which draws the slider knob. See the :class:`matplotlib.patches.Rectangle` documentation valid property names (eg, *facecolor*, *edgecolor*, *alpha*, ...). Parameters ---------- ax : Axes The Axes to put the slider in label : str Slider label valmin : float The minimum value of the slider valmax : float The maximum value of the slider valinit : float The slider initial position label : str The slider label valfmt : str Used to format the slider value, fprint format string closedmin : bool Indicate whether the slider interval is closed on the bottom closedmax : bool Indicate whether the slider interval is closed on the top slidermin : Slider or None Do not allow the current slider to have a value less than `slidermin` slidermax : Slider or None Do not allow the current slider to have a value greater than `slidermax` dragging : bool if the slider can be dragged by the mouse """ AxesWidget.__init__(self, ax) self.valmin = valmin self.valmax = valmax self.val = valinit self.valinit = valinit self.poly = ax.axhspan(valmin, valinit, 0, 1, **kwargs) self.hline = ax.axhline(valinit, 0, 1, color='r', lw=1) self.valfmt = valfmt ax.set_xticks([]) ax.set_ylim((valmin, valmax)) ax.set_yticks([]) ax.set_navigate(False) self.connect_event('button_press_event', self._update) self.connect_event('button_release_event', self._update) if dragging: self.connect_event('motion_notify_event', self._update) self.label = ax.text(0.5, 1.03, label, transform=ax.transAxes, verticalalignment='center', horizontalalignment='center') self.valtext = ax.text(0.5, -0.03, valfmt % valinit, transform=ax.transAxes, verticalalignment='center', horizontalalignment='center') self.cnt = 0 self.observers = {} self.closedmin = closedmin self.closedmax = closedmax self.slidermin = slidermin self.slidermax = slidermax self.drag_active = False def _update(self, event): """update the slider position""" if self.ignore(event): return if event.button != 1: return if event.name == 'button_press_event' and event.inaxes == self.ax: self.drag_active = True event.canvas.grab_mouse(self.ax) if not self.drag_active: return elif ((event.name == 'button_release_event') or (event.name == 'button_press_event' and event.inaxes != self.ax)): self.drag_active = False event.canvas.release_mouse(self.ax) return val = event.ydata if val <= self.valmin: if not self.closedmin: return val = self.valmin elif val >= self.valmax: if not self.closedmax: return val = self.valmax if self.slidermin is not None and val <= self.slidermin.val: if not self.closedmin: return val = self.slidermin.val if self.slidermax is not None and val >= self.slidermax.val: if not self.closedmax: return val = self.slidermax.val self.set_val(val) def set_val(self, val): xy = self.poly.xy xy[1] = 0, val xy[2] = 1, val self.poly.xy = xy self.valtext.set_text(self.valfmt % val) if self.drawon: self.ax.figure.canvas.draw_idle() self.val = val if not self.eventson: return for cid, func in six.iteritems(self.observers): func(val) def on_changed(self, func): """ When the slider value is changed, call *func* with the new slider position A connection id is returned which can be used to disconnect """ cid = self.cnt self.observers[cid] = func self.cnt += 1 return cid def disconnect(self, cid): """remove the observer with connection id *cid*""" try: del self.observers[cid] except KeyError: pass def reset(self): """reset the slider to the initial value if needed""" if (self.val != self.valinit): self.set_val(self.valinit)