代码之家  ›  专栏  ›  技术社区  ›  Karlovsky120

在类中放置带有可控制参数的动画图形

  •  0
  • Karlovsky120  · 技术社区  · 7 年前
    import matplotlib.gridspec as gridspec
    import numpy as np
    
    from matplotlib import animation
    from matplotlib import pyplot as plt
    from matplotlib.widgets import Slider, CheckButtons
    
    PI = np.pi
    
    sliderDataList = [{'name': 'Left amplitude', 'min': 0.1, 'max': 8.0, 'init': 2, 'step': 0.01}]
    checkboxDataList = [{'name': 'Left wave', 'init': True}]
    
    
    class CollidingWaves:
        def __init__(self, timeFactor=5, x_range=4 * PI, x_offset=0, y_range=4, y_offset=0, sliderData=[],
                     checkboxData=[], tension=1, massDensity=1):
            self.x_range = x_range
            self.x_offset = x_offset
            self.y_range = y_range
            self.y_offset = y_offset
            self.sliderData = sliderData
            self.checkboxData = checkboxData
            self.tension = tension
            self.massDensity = massDensity
            self.timeFactor = timeFactor
            self.showWave = []
            self.amplitude = 0
    
            self.fig = plt.figure()
    
            self.mainGrid = gridspec.GridSpec(2, 1)
            self.graphCell = plt.subplot(self.mainGrid[0, :])
            self.graphCell.set(xlim=(-self.x_range - self.x_offset, self.x_range - self.x_offset),
                               ylim=(-self.y_range - self.y_offset, self.y_range - self.y_offset))
    
            self.x_data = np.linspace(-self.x_range - self.x_offset, self.x_range - self.x_offset, 512)
            self.y_data = []
    
            self.lines = [plt.plot([], [])[0] for _ in range(2)]
            self.patches = self.lines
    
            self.controlCell = self.mainGrid[1, :]
            self.controlGrid = gridspec.GridSpecFromSubplotSpec(1, 7, self.controlCell)
    
            self.checkboxCell = self.controlGrid[0, 0]
            self.checkboxGrid = gridspec.GridSpecFromSubplotSpec(1, 1, self.checkboxCell)
            self.checkboxes = []
            self.checkboxAx = plt.subplot(self.checkboxGrid[0, 0:1])
            self.checkbox = CheckButtons(self.checkboxAx, tuple(x["name"] for x in self.checkboxData),
                                         tuple(x["init"] for x in self.checkboxData))
            self.checkboxes.append(self.checkbox)
    
            self.sliderCell = self.controlGrid[0, 2:6]
            self.sliderGrid = gridspec.GridSpecFromSubplotSpec(len(self.sliderData), 1, self.sliderCell)
            self.sliders = []
            for i in range(0, len(self.sliderData)):
                self.sliderAx = plt.subplot(self.sliderGrid[i, 0])
                self.slider = Slider(self.sliderAx, self.sliderData[i]["name"], self.sliderData[i]["min"],
                                     self.sliderData[i]["max"], valinit=self.sliderData[i]["init"],
                                     valstep=self.sliderData[i]["step"])
                self.sliders.append(self.slider)
    
            for slider in self.sliders:
                slider.on_changed(self.update)
            for checkbox in self.checkboxes:
                checkbox.on_clicked(self.update)
    
        def update(self):
            self.amplitude = self.sliders[0].val
            self.showWave = self.checkboxes[0].val
    
        def init(self):
            for line in self.lines:
                line.set_data([], [])
            return self.patches
    
        def animate(self, i):
            self.y_data[0] = [1] * 512
            self.y_data[1] = [2] * 512
            self.lines[0].set_data(self.x_data, self.y_data[0])
            self.lines[1].set_data(self.x_data, self.y_data[1])
    
            return self.patches
    
        def start(self):
            animation.FuncAnimation(self.fig, self.animate, init_func=self.init, frames=600, repeat=True, interval=20, blit=True)
            plt.show()
    
    
    graph = CollidingWaves(sliderData=sliderDataList, checkboxData=checkboxDataList)
    graph.start()
    

    上面截取的想法是有一个动画图形和一组控制其参数的小部件。更改参数应更改正在显示的图形。

    也就是说,上面的代码不做这些。它是一个不改变的动画图形和两个改变对象内变量的小部件。然而,该计划并没有像预期的那样起作用。

    首先,图形根本不显示。我不明白为什么。 其次,更改两个小部件中任何一个的状态都会引发 TypeError :

    Traceback (most recent call last):
      File "C:\Programs\Python37\lib\site-packages\matplotlib\cbook\__init__.py", line 215, in process
        func(*args, **kwargs)
      File "C:\Programs\Python37\lib\site-packages\matplotlib\widgets.py", line 417, in _update
        self.set_val(val)
      File "C:\Programs\Python37\lib\site-packages\matplotlib\widgets.py", line 438, in set_val
        func(val)
    TypeError: update() takes 1 positional argument but 2 were given
    

    1 回复  |  直到 7 年前
        1
  •  1
  •   ImportanceOfBeingErnest    7 年前

    这里似乎只有四个问题:

    • update
    • 该复选框没有一个 val 属性您可以通过获取复选框的状态 .get_status .
    • y_data 分配了两个元素。因此,它从一开始就需要有两个元素。
    • 动画需要保留在内存中。因此,您可以将其分配给一个变量。

    总的来说,这将起作用:

    import matplotlib.gridspec as gridspec
    import numpy as np
    
    from matplotlib import animation
    from matplotlib import pyplot as plt
    from matplotlib.widgets import Slider, CheckButtons
    
    PI = np.pi
    
    sliderDataList = [{'name': 'Left amplitude', 'min': 0.1, 'max': 8.0, 'init': 2, 'step': 0.01}]
    checkboxDataList = [{'name': 'Left wave', 'init': True}]
    
    
    class CollidingWaves:
        def __init__(self, timeFactor=5, x_range=4 * PI, x_offset=0, y_range=4, y_offset=0, sliderData=[],
                     checkboxData=[], tension=1, massDensity=1):
            self.x_range = x_range
            self.x_offset = x_offset
            self.y_range = y_range
            self.y_offset = y_offset
            self.sliderData = sliderData
            self.checkboxData = checkboxData
            self.tension = tension
            self.massDensity = massDensity
            self.timeFactor = timeFactor
            self.showWave = []
            self.amplitude = 0
    
            self.fig = plt.figure()
    
            self.mainGrid = gridspec.GridSpec(2, 1)
            self.ax = plt.subplot(self.mainGrid[0, :])
            self.ax.set(xlim=(-self.x_range - self.x_offset, self.x_range - self.x_offset),
                               ylim=(-self.y_range - self.y_offset, self.y_range - self.y_offset))
    
            self.x_data = np.linspace(-self.x_range - self.x_offset, self.x_range - self.x_offset, 512)
            self.y_data = [[],[]]
    
            self.lines = [self.ax.plot([], [])[0] for _ in range(2)]
            self.patches = self.lines
    
            self.controlCell = self.mainGrid[1, :]
            self.controlGrid = gridspec.GridSpecFromSubplotSpec(1, 7, self.controlCell)
    
            self.checkboxCell = self.controlGrid[0, 0]
            self.checkboxGrid = gridspec.GridSpecFromSubplotSpec(1, 1, self.checkboxCell)
            self.checkboxes = []
            self.checkboxAx = plt.subplot(self.checkboxGrid[0, 0:1])
            self.checkbox = CheckButtons(self.checkboxAx, tuple(x["name"] for x in self.checkboxData),
                                         tuple(x["init"] for x in self.checkboxData))
            self.checkboxes.append(self.checkbox)
    
            self.sliderCell = self.controlGrid[0, 2:6]
            self.sliderGrid = gridspec.GridSpecFromSubplotSpec(len(self.sliderData), 1, self.sliderCell)
            self.sliders = []
            for i in range(0, len(self.sliderData)):
                self.sliderAx = plt.subplot(self.sliderGrid[i, 0])
                self.slider = Slider(self.sliderAx, self.sliderData[i]["name"], self.sliderData[i]["min"],
                                     self.sliderData[i]["max"], valinit=self.sliderData[i]["init"],
                                     valstep=self.sliderData[i]["step"])
                self.sliders.append(self.slider)
    
            for slider in self.sliders:
                slider.on_changed(self.update)
            for checkbox in self.checkboxes:
                checkbox.on_clicked(self.update)
    
        def update(self, event=None):
            self.amplitude = self.sliders[0].val
            self.showWave = self.checkboxes[0].get_status()
    
        def init(self):
            for line in self.lines:
                line.set_data([], [])
            return self.patches
    
        def animate(self, i):
            self.y_data[0] = [1] * 512
            self.y_data[1] = [2] * 512
            self.lines[0].set_data(self.x_data, self.y_data[0])
            self.lines[1].set_data(self.x_data, self.y_data[1])
    
            return self.patches
    
        def start(self):
            self.ani = animation.FuncAnimation(self.fig, self.animate, init_func=self.init, frames=600, repeat=True, interval=20, blit=True)
            plt.show()
    
    
    graph = CollidingWaves(sliderData=sliderDataList, checkboxData=checkboxDataList)
    graph.start()