2

Inside the call-back associated with a matplotlib.widgets.Slider instance, I want to modify the Slider valmin, valmax and val attributes, and then re-draw the Slider. When I try to re-draw it, nothing happens.

Before the call-back completes, I call plt.draw() and, while the rest of the plot is correctly updated, the Slider looks the same as before. I also tried calling draw() on the Axes of the Slider, and again no visible change.

How can I force a re-draw of the Slider instance after changing its attributes?

Thansk!

UPDATE

Here a small program that demonstrates the issue (with a crude dialog). The wanted behavior is that by clicking on a different value of the radio button, the slider changes range of values, current value and label. What happens instead is that when clicking on "Gradient" the slider stops responding (cannot be dragged anymore), and when clicking on "Gradient" or "Channel" the slider is not updated, in spite of a call to plt.draw()

from matplotlib import pyplot as plt
from matplotlib import gridspec
from matplotlib.widgets import Slider, RadioButtons

# Set the grid
grid = gridspec.GridSpec(2, 1, height_ratios=[1, 2])

# Plot the sliders
axes_slider = plt.subplot(grid[0, 0])
slider = Slider(axes_slider, 'Channel', valmin=0, valmax=255, valinit=128)

# Plot the radio buttons
axes_button = plt.subplot(grid[1, 0])
button = RadioButtons(axes_button, ('Gradient', 'Channel'), active=1)

plt.tight_layout(h_pad=0)
plt.subplots_adjust(left=.2, right=.9)

def update(_):
    take_gradient = True if button.value_selected == 'Gradient' else False
    if take_gradient:
        slider.valmin = -1
        slider.valmax = 1
        slider.val = 0
        slider.label= 'Gradient'
        slider.valinit = 0
    else:
        slider.valmin = 0
        slider.valmax = 255
        slider.val = 128
        slider.label = 'Channel'
        slider.valinit = 128
    plt.draw()

# Register call-backs with widgets
slider.on_changed(update)
button.on_clicked(update)

plt.show()
Fanta
  • 2,825
  • 5
  • 26
  • 35

1 Answers1

2

The slider itself is a class that hosts and manages some parameters. It does not have a draw method.

That said, it's not enough to only set the parameters of the slider, you would also need to update the axes the slider lives in. At the end this all sums up at doing exactly the steps the slider is doing a initialization, so you can simple call the silder's __init__ function.

I would strongly recommend to use two different functions, one for changing the slider that is called upon button clicks and one for things that should happen if the slider value is adjusted.

Here is a working example:

from matplotlib import pyplot as plt
from matplotlib import gridspec
from matplotlib.widgets import Slider, RadioButtons

# Set the grid
grid = gridspec.GridSpec(2, 1, height_ratios=[1, 2])

# Plot the sliders
axes_slider = plt.subplot(grid[0, 0])
slider = Slider(axes_slider, 'Channel', valmin=0, valmax=255, valinit=128)

# Plot the radio buttons
axes_button = plt.subplot(grid[1, 0])
button = RadioButtons(axes_button, ('Gradient', 'Channel'), active=1)

plt.tight_layout(h_pad=0)
plt.subplots_adjust(left=.2, right=.9)

def buttonupdate(val):
    if val == "Gradient":
        axes_slider.clear()
        slider.__init__(axes_slider, 'Gradient', valmin=-1, valmax=1, valinit=0)        
    else:
        axes_slider.clear()
        slider.__init__(axes_slider, 'Channel', valmin=0, valmax=255, valinit=128) 
    plt.gcf().canvas.draw_idle()

def sliderupdate(val):
    if slider.label.get_text() == 'Gradient':
        #do something depending on gradient value
        pass
    else:
        #do something depending on channel value
        pass

# Register call-backs with widgets
slider.on_changed(sliderupdate)
button.on_clicked(buttonupdate)

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712