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

The idea of the snipped above is to have an animated graph and a set of widgets that control it's parameters. Changing the parameters should change the graph that is being displayed.

That being said, the code above does none of that. It's a animated graph that doesn't change and two widgets that change variables within the object. However, the program does not work as expected.

First of all, graph is not displayed at all. I don't understand why. Secondly, changing state of any of the two widgets throws a 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

What am I doing wrong here?

Karlovsky120
  • 6,212
  • 8
  • 41
  • 94

1 Answers1

1

Seems there are only four issues here:

  • update is called with an event as argument. You need to make sure the it actually takes this argument, even if you don't use it.
  • The checkbox does not have a val attribute. You get the status of the checkbox via .get_status.
  • The y_data is assigned two elements. Hence it needs to have two elements from the start.
  • The animation needs to stay in memory. Hence you would assign it to a variable.

In total, this would work:

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()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712