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?
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.val
attribute. You get the status of the checkbox via .get_status
.y_data
is assigned two elements. Hence it needs to have two elements from the start.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()