3

I use matplotlib to generate a simple plot with 2 sliders which control a moving point on the plot - showing the user how a change in x or y affects the location of the point. I need these to sliders to share information with one another - if there is a change in one, the other one updates its values based on the changed value (e.g. if the slider that controls x changes, then the slider that controls y is updated, and vice versa).

I've researched this on stackoverflow and tried to implement two solutions (first attempt is commented out in the below code) without success. The last iteration of my attempt seems to work, but after a while it freezes the plot.

My code is:

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)
# Define x values and y function, y = f(2x**2)
x = np.arange(0.0, 11., 1.)
y = x**2
fxplot, = plt.plot(x, y, lw=2)

#define the bullet point to slide on the function
x0=2.
y0=x0**2
x0_old = x0
y0_old = y0
ptplot, = plt.plot(x0, y0, 'ko')

# Define the sliders
axcolor = 'lightgoldenrodyellow'
ax_x = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
ax_y = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)

xSlider = Slider(ax_x, 'x', 0.0, 10.0, valinit=x0, valstep=1)
ySlider = Slider(ax_y, 'y', 0.0, 10.0**2, valinit=y0, valstep=1)

# Update function; movement of one slider updates the other
# by reinitializing it
def update(val):
    global x0_old, y0_old
    x0 = xSlider.val
    y0 = ySlider.val
    
#   Detect change in y0
    if x0 == x0_old:
        print('y0 changed',y0,y0_old)
        ptplot.set_xdata(np.sqrt(y0))
        ptplot.set_ydata(y0)
        y0_old = y0
##        ax_x.clear()
##        xSlider.__init__(ax_x, 'x', 0.0, 10.0, valinit=np.sqrt(y0), valstep=1) 
        xSlider.valinit = np.sqrt(y0)
        xSlider.reset()
        
#   Detect change in x0
    if y0 == y0_old:
        print('x0 changed',x0,x0_old)
        ptplot.set_xdata(x0)
        ptplot.set_ydata(x0**2)        
        x0_old = x0
##        ax_y.clear()
##        ySlider.__init__(ax_y, 'y', 0.0, 10.0**2, valinit=y0, valstep=1)
        ySlider.valinit = x0**2
        ySlider.reset()

    print('\n')
    plt.gcf().canvas.draw_idle()

xSlider.on_changed(update)
ySlider.on_changed(update)

plt.show()

I hope this is a simple fix, but unfortunately, I'm unable to see it and I hoping there is someone out there with more experience using sliders that is able to assist.

Thanks in advance.

1 Answer 1

8

The obvious solultion is to call Slider.set_val(), however, that creates an infinite recursion error since calling the set_val() triggers the callback, which calls set_val(), etc...

Thankfully, the error can be prevented by instructing the widget to not call the callback function using the (undocumented) property Widget.eventson

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)
# Define x values and y function, y = f(2x**2)
x = np.arange(0.0, 11., 1.)
f = lambda x: x**2
g = lambda x: np.sqrt(x)
y = f(x)
fxplot, = plt.plot(x, y, lw=2)

#define the bullet point to slide on the function
x0=2.
y0=f(x0)
ptplot, = plt.plot(x0, y0, 'ko')

# Define the sliders
axcolor = 'lightgoldenrodyellow'
ax_x = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
ax_y = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)

xSlider = Slider(ax_x, 'x', 0.0, 10.0, valinit=x0, valstep=1)
ySlider = Slider(ax_y, 'y', f(0.0), f(10.0), valinit=y0, valstep=1)

def update_x(x):
    y = f(x)
    ptplot.set_data(x, y)
    ySlider.eventson = False
    ySlider.set_val(y)
    fig.canvas.draw()
    ySlider.eventson = True

def update_y(y):
    x = g(y)
    ptplot.set_data(x, y)
    xSlider.eventson = False
    xSlider.set_val(x)
    fig.canvas.draw()
    xSlider.eventson = True

xSlider.on_changed(update_x)
ySlider.on_changed(update_y)

plt.show()
Sign up to request clarification or add additional context in comments.

2 Comments

And if I wish to change the values of valmin, valmax, or valinit after slider is updated how is this accomplished?
There doesn't seem to be a way to change those properties

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.