【问题标题】:Add a vertical slider with matplotlib使用 matplotlib 添加垂直滑块
【发布时间】:2014-11-14 01:40:31
【问题描述】:

我想用 matplotlib 创建一个垂直滑块而不是水平滑块。

我在 matplotlib 网页 http://matplotlib.org/examples/widgets/slider_demo.html 中找到了一个很好的例子,但我不知道如何在 Y 轴上移动滑块并更改滑块标签。我可以更改轴的位置,但不能更改滑块的移动。例子在这里:

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()

【问题讨论】:

    标签: python matplotlib widget slider


    【解决方案1】:

    从 matplotlib 3.1 开始,有一个方向关键字。在 matplotlib 3.1 之前,这是不可能开箱即用的,因为 matplotlib.widgets.Slider 实现使用 axvspanaxvline 来定义滑块(这是一个 patches.Polygon),并根据水平假设对其进行了更新。

    如果你还在使用旧版本的matplotlib,以水平滑块为例编写你自己的垂直滑块并不难(你也可以从AxesWidget子类化),但必须自己完成.

    自 matplotlib 2.0 起有效: 下面给出了一个垂直滑块类;它就像水平的一样工作,除了它是......好吧......垂直!

    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 (e.g., *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)
    

    【讨论】:

    • set_val 方法中,我不得不更改:for cid, func in self.observers.iteritems():for cid, func in six.iteritems(self.observers):self.ax.figure.canvas.draw()self.ax.figure.canvas.draw_idle()
    • 确实,这只是为了兼容 Python 2 而不是 3。感谢您的评论!
    • 有没有办法用这个类轻松模拟valstep
    【解决方案2】:

    我知道这篇文章已有 5 年的历史,但在尝试实现我自己的垂直滑块后,我发现截至 matplotlib 3.1 这个功能本身就存在。

    我有 matplotlib 3.1.2,其中一个参数是

    方向:str,“水平”或“垂直”,默认值:“水平” 滑块的方向。

    请看这里https://matplotlib.org/3.1.1/api/widgets_api.html

    【讨论】:

    • 这应该是新接受的答案。与 matplotlib 3.1.3 配合得非常好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    相关资源
    最近更新 更多