【问题标题】:Matplotlib slider widgets (user-defined script-generated) aren't triggering recalculation and plot updateMatplotlib 滑块小部件(用户定义的脚本生成)不会触发重新计算和绘图更新
【发布时间】:2019-07-09 21:15:54
【问题描述】:

这是一个高度抽象的主程序和模块。 Matplotlib 滑块小部件应导致 Data 实例重新计算,然后绘图应更新。

每次更新滑块时,它都应将其新值传递给在滑块实例化期间定义的适当方法。例如,移动第一个滑块应将其值发送到d1.set_a(),这会触发对该数据的重新计算,然后应触发P.offsets()(请参阅模块)以更新绘图。

问题:我怎样才能让这些用户定义的、脚本生成的滑块来触发数据对象和图的更新? Slider Widgets 实例是否提供比我在这里做的方式更方便的方法?

主程序:

import numpy as np
from MODULE import Data, Plot

x0 = np.linspace(0, 10., 11)
y1, y2 = [0.5 * (1.0 + f(x0)) for f in (np.cos, np.sin)]

d1, d2 = Data('hey', x0, y1), Data('wow', x0, y2) # data generating objects

p = Plot('hey')    # plot object

p.add_slider(d1, 'set_a', d1.a, (0.2, 1.0))
p.add_slider(d1, 'set_p', d1.p, (0.5, 2.0))
p.add_slider(d2, 'set_a', d2.a, (0.2, 1.0))
p.add_slider(d2, 'set_p', d2.p, (0.5, 2.0))

p.plotme((d1, d2))

当前结果,滑块移动但不触发重新计算/重新绘制:

模块:

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

class Plot(object):
    def __init__(self, name):
        self.name = name
        self.sliders = []
        self.axcolor = 'lightgoldenrodyellow'
        self.fig = plt.figure()

    def sliderfunc(self, value):
        print('sliderfunc')       # currently not getting here
        for (obj, P) in self.OPs:
            P.set_offsets(obj.xy.T)
        self.fig.canvas.draw_idle()

    def add_slider(self, obj, method, nominal, limits):
        ybot = 0.03 * (len(self.sliders) + 1)
        name = obj.name + '.' + method 
        ax_slider   = plt.axes([0.25, ybot, 0.50, 0.02], facecolor=self.axcolor)
        slider      = Slider(ax_slider, name, limits[0], limits[1],
                             valinit=nominal)
        slider.on_changed(getattr(obj, method)) # this may not be right
        self.sliders.append(slider)
        return slider

    def plotme(self, objs):
        ybot = 0.03 * (len(self.sliders) + 3)
        A = plt.axes([0.15, ybot, 0.65, 0.50])
        self.OPs = []
        for obj in objs:
            P = A.scatter(obj.x, obj.y)
            self.OPs.append((obj, P))
        plt.show()

class Data(object):
    def __init__(self, name, x0, y0):
        self.name = name
        self.a = 1.0
        self.p = 1.0
        self.x0, self.y0 = x0, y0
        self.setstuff(a=1.0, p=1.0)
    def setstuff(self, a=None, p=None):
        if a != None:
            self.set_a(a)
        if p != None:
            self.set_p(p)
    def set_a(self, a):
        self.a = a
        self.x = self.a * self.x0
        self.y = self.y0**self.p
        self.xy = np.vstack((self.x, self.y))
    def set_p(self, p):
        self.p = p
        self.x = self.a * self.x0
        self.y = self.y0**self.p
        self.xy = np.vstack((self.x, self.y))

【问题讨论】:

  • 可能您只是忘记将Slider 分配给变量。如果你不这样做,它将被垃圾收集(因为所有没有引用的对象都会在 python 中)并在创建后立即从内存中删除。
  • @ImportanceOfBeingErnest 不,不是这样。 p.sliders 列表的内容仍然包含四个 matplotlib.widgets.Slider 实例,我仍然可以读取它们的数据。
  • 好的,所以您需要提供一个可运行的代码,以便实际测试。 (目前有一些未定义的变量,所以我无法运行它)
  • 我在 sliderfunc 中显示 O 和 fig undefined。奇怪的是,即使我调整滑块,我也没有得到回溯。在 ipython 和终端中的结果相同。
  • Spyder 将它们标记为错误。显然,sliderfunc 永远不会被调用。看起来 sliderfunc 和 add_slider 都有尝试更新绘图的代码。我在这里超出了我的专业领域,但无法抗拒尝试您的代码。我也使用 Anaconda python。

标签: python python-3.x matplotlib widget


【解决方案1】:

各个滑块需要执行不同的操作,因此每个滑块都需要自己的回调。在其中你当然可以调用相同的函数 (sliderfunc) 来更新绘图。

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


class Plot(object):
    def __init__(self, name):
        self.name = name
        self.sliders = []
        self.fig = plt.figure()

    def sliderfunc(self):
        for (obj, P) in self.OPs:
            P.set_offsets(obj.xy.T)
        self.fig.canvas.draw_idle()

    def add_slider(self, obj, method, nominal, limits):
        ybot = 0.03 * (len(self.sliders) + 1)
        name = obj.name + '.' + method 
        ax_slider   = plt.axes([0.25, ybot, 0.50, 0.02], facecolor="w")
        slider      = Slider(ax_slider, name, limits[0], limits[1],
                             valinit=nominal)

        def callback(val):
            getattr(obj, method)(val)
            self.sliderfunc()

        slider.on_changed(callback)
        self.sliders.append(slider)
        return slider

    def plotme(self, objs):
        ybot = 0.03 * (len(self.sliders) + 3)
        A = plt.axes([0.15, ybot, 0.65, 0.50])
        self.OPs = []
        for obj in objs:
            P = A.scatter(obj.x, obj.y)
            self.OPs.append((obj, P))
        plt.show()

class Data(object):
    def __init__(self, name, x0, y0):
        self.name = name
        self.a = 1.0
        self.p = 1.0
        self.x0, self.y0 = x0, y0
        self.update()

    def update(self):
        self.x = self.a * self.x0
        self.y = self.y0**self.p
        self.xy = np.vstack((self.x, self.y))

    def set_a(self, val):
        self.a = val
        self.update()

    def set_p(self, val):
        self.p = val
        self.update()


x0 = np.linspace(0, 10., 11)
y1, y2 = [0.5 * (1.0 + f(x0)) for f in (np.cos, np.sin)]

d1, d2 = Data('hey', x0, y1), Data('wow', x0, y2) # data generating objects

p = Plot('hey')    # plot object

p.add_slider(d1, 'set_a', d1.a, (0.2, 1.0))
p.add_slider(d1, 'set_p', d1.p, (0.5, 2.0))
p.add_slider(d2, 'set_a', d2.a, (0.2, 1.0))
p.add_slider(d2, 'set_p', d2.p, (0.5, 2.0))

p.plotme((d1, d2))

【讨论】:

  • 哦,这真的,真的很漂亮,哇!我想我要花很长时间才能弄清楚这一点,但现在我明白了。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-22
  • 2020-06-30
  • 2022-01-11
  • 2016-11-06
  • 2021-05-06
  • 1970-01-01
相关资源
最近更新 更多