Search code examples
pythonmatplotlibslidermatplotlib-widget

matplotlib multiple dependent sliders


i have two sliders setup, one to control the position on the plot and one to control the scale of the x-axis. this works great, except i am using a different dataframe to control the slider than what the x-axis actually is, as it is in excel datetime and i could not get it to work with the sliders. i am doing this by by creating another df, slider, the same length of the df used by the x-asix, time. the issue i am running into is when the sum of the two slider values is above 100, the indexed value would be outside of the time df. i have tried using a solution found here, but can only control one slider that way, and the scale slider will still move past a sum of 100. any suggestions on limiting the top slider would be appreciated

cheers

class slider_limit(object):
    def __init__(self, slider, func):
        self.func, self.slider = func, slider
    @property
    def val(self):
        return self.func(self.slider.val)
    
ax_scale = plt.axes([0.2, 0.1, 0.65, 0.03])
slider_scale = Slider(ax_scale, 'Scale', 0.0, 100, valstep=100/(len(data.slider)-1), valinit=1, slidermax=slider_limit(slider_pos, lambda x: 100 - x))

ax_pos = plt.axes([0.2, 0.05, 0.65, 0.03])
slider_pos = Slider(ax_pos, 'Position', 0.0, 100, valstep=100/(len(data.slider)-1), valinit=0, slidermax=slider_limit(slider_scale, lambda x: 100 - x))

def update(val):
    scale_index = data[data['slider'] == slider_scale.val].index[0]
    pos_index = data[data['slider'] == slider_pos.val].index[0]
    scale = data.time[pos_index + scale_index]
    pos = data.time[pos_index]
    slider_sum = slider_scale.val + slider_pos.val
    ax.set_xlim(pos, scale)
    fig.canvas.draw_idle()
        
slider_scale.on_changed(update)
slider_pos.on_changed(update)

Solution

  • I was able to do this by setting slidermax outside of where the slider was initially setup:

    class slider_limit(object):
        def __init__(self, slider, func):
            self.func, self.slider = func, slider
        @property
        def val(self):
            return self.func(self.slider.val)
    
    slider_scale = Slider(ax_scale, 'scale', 0.0, 100, valinit=1)
    slider_pos = Slider(ax_pos, 'position', 0.0, 100, valinit=0)
    
    slider_scale.slidermax = slider_limit(slider_pos, lambda x: 100 - x)
    slider_pos.slidermax = slider_limit(slider_scale, lambda x: 100 - x)
    
    def update(val):
        scale_index = data[data['slider'] == slider_scale.val].index[0]
        pos_index = data[data['slider'] == slider_pos.val].index[0]
        scale = data.time[pos_index + scale_index]
        pos = data.time[pos_index]
        ax1.set_xlim(pos, scale)
        fig.canvas.draw_idle()
    
    slider_scale.on_changed(update)
    slider_pos.on_changed(update)