Matplotlib 根据另一个滑块的变化更新一个滑块

Matplotlib update one slider based on change in another slider

我使用 matplotlib 生成一个简单的绘图,其中有 2 个滑块控制绘图上的移动点 - 向用户展示 x 或 y 的变化如何影响点的位置。我需要这些滑块来彼此共享信息——如果一个滑块发生变化,另一个滑块会根据更改后的值更新其值(例如,如果控制 x 的滑块发生变化,则控制 y 的滑块也会更新,反之亦然)。

我在 Whosebug 上对此进行了研究,并尝试实施两种解决方案(第一次尝试在下面的代码中被注释掉)但没有成功。我尝试的最后一次迭代似乎有效,但过了一会儿它冻结了情节。

我的代码是:

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

我希望这是一个简单的修复,但不幸的是,我看不到它,我希望有更多使用滑块经验的人能够提供帮助。

提前致谢。

最明显的解决方案是调用 Slider.set_val(),但是,这会产生无限递归错误,因为调用 set_val() 会触发回调,调用 set_val(),等等...

幸运的是,可以通过使用 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()