pythonmatplotlibslider

Matplotlib slider not working when plot is updated in separate function


I am using matplotlib's Slider to do a simple dynamic plot of a sine curve. I want to change the frequency using a slider. The code looks like this:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

x = np.linspace(400, 800, 400)

fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.25)
mod = 1.
lambda_plot, = ax.plot(x, np.sin(mod*x*2*np.pi/500))


ax_mod_slider = fig.add_axes([0.3, 0.1, 0.5, 0.04])
mod_slider = Slider(
    ax = ax_mod_slider,
    label = "modulation",
    valmin = 0,
    valmax = 20,
    valinit = 1.,
    orientation = "horizontal")

def update_mod(val):
    mod = mod_slider.val
    redraw()
    fig.canvas.draw_idle()
    
    
def redraw():
    lambda_plot.set_ydata(np.sin(mod*x*2*np.pi/500))
    
    
mod_slider.on_changed(update_mod)

plt.show()

This does only work if I put the code in redraw() directly in update_mod(), like this:

def update_mod(val):
    mod = mod_slider.val
    lambda_plot.set_ydata(np.sin(mod*x*2*np.pi/500))
    fig.canvas.draw_idle()

Why can I not call another function for changing the plot? In this example, I could put it all into update_mod(), but as I do more complicated calculations, I thought it might be good to be able to split update_mod() into separate functions.


Solution

  • In the redraw() function:

    def redraw():
        lambda_plot.set_ydata(np.sin(mod * x * 2 * np.pi / 500))
    

    You're using the variable mod, but this mod is not defined in the local scope of redraw(), nor is it properly declared as global or passed as an argument. So Python looks for it in the outer scope and finds the mod = 1. you defined at the beginning of the script which never changes.

    When update_mod() changes mod, it does so in its local scope, and that doesn't affect the mod that redraw() sees.

    Just pass the mod value and it should work properly:

    def redraw(mod):
        lambda_plot.set_ydata(np.sin(mod * x * 2 * np.pi / 500))