Closed
Description
Bug summary
I have the same code with two different outcomes, the code runs in Jupyter, the reason is because it uses matplotlib.animation, (I have the same outcome when using it from TkInter).
Code for reproduction
This code works, based on stackoverflow
import IPython
import numpy as np
import matplotlib.animation as animation
from matplotlib.figure import Figure
from mpl_toolkits.axisartist.parasite_axes import HostAxes, ParasiteAxes
import itertools
import scipy.signal
class TKWindow():
"""Das hier zeigt in tkinter ein Plot von Spannung und Strom """
dictWidgets=dict()
def createWidgets(self):
self.figure = Figure(figsize=(10,7), dpi=100)
#self.canvas = FigureCanvasTkAgg(self.figure, self).get_tk_widget()
self.figure.tight_layout()
def __init__(self, title = "Test Window"):
self.createWidgets()
self.onSelection("sinus")
def window_loop(self):
self.animation_object = animation.FuncAnimation(self.figure, self.animate,init_func=self.create_axes, interval=1,frames=itertools.count(step=0.001))
#self.mainloop()
def animate(self, i):
self.frequency=50
self.amplitude=1
for ax in self.axes:
for artist in ax.lines + ax.collections:
artist.remove()
def onSelection(self,a):
this=self
def sine(amplitude,frequency,time):
return amplitude*np.sin(time*2*np.pi*frequency)
this.dictWidgets["someFunction"]=sine
def create_axes(self):
#Creating axes
self.axes=[self.figure.add_subplot(1,1,1)]
self.axes.append(self.axes[0].twinx())
self.axes.append(self.axes[0].twinx())
exercise_06 = TKWindow()
exercise_06.window_loop()
display(IPython.display.HTML(exercise_06.animation_object.to_html5_video()))
exercise_06.animation_object
if you change the line
self.axes=[self.figure.add_subplot(1,1,1)]
to
self.axes=[self.figure.add_subplot(1,1,1, axes_class=HostAxes)]
the code will no longer work
Actual outcome
File ~\.virtualenvs\mdv-RxgQ3xiL\lib\site-packages\matplotlib\animation.py:1268, in Animation.to_html5_video(self, embed_limit)
1264 Writer = writers[mpl.rcParams['animation.writer']]
1265 writer = Writer(codec='h264',
1266 bitrate=mpl.rcParams['animation.bitrate'],
1267 fps=1000. / self._interval)
-> 1268 self.save(str(path), writer=writer)
1269 # Now open and base64 encode.
1270 vid64 = base64.encodebytes(path.read_bytes())
File ~\.virtualenvs\mdv-RxgQ3xiL\lib\site-packages\matplotlib\animation.py:1089, in Animation.save(self, filename, writer, fps, dpi, codec, bitrate, extra_args, metadata, extra_anim, savefig_kwargs, progress_callback)
1086 for data in zip(*[a.new_saved_frame_seq() for a in all_anim]):
1087 for anim, d in zip(all_anim, data):
1088 # TODO: See if turning off blit is really necessary
-> 1089 anim._draw_next_frame(d, blit=False)
1090 if progress_callback is not None:
1091 progress_callback(frame_number, total_frames)
File ~\.virtualenvs\mdv-RxgQ3xiL\lib\site-packages\matplotlib\animation.py:1124, in Animation._draw_next_frame(self, framedata, blit)
1120 def _draw_next_frame(self, framedata, blit):
1121 # Breaks down the drawing of the next frame into steps of pre- and
1122 # post- draw, as well as the drawing of the frame itself.
1123 self._pre_draw(framedata, blit)
-> 1124 self._draw_frame(framedata)
1125 self._post_draw(framedata, blit)
File ~\.virtualenvs\mdv-RxgQ3xiL\lib\site-packages\matplotlib\animation.py:1718, in FuncAnimation._draw_frame(self, framedata)
1714 self._save_seq = self._save_seq[-self.save_count:]
1716 # Call the func with framedata and args. If blitting is desired,
1717 # func needs to return a sequence of any artists that were modified.
-> 1718 self._drawn_artists = self._func(framedata, *self._args)
1720 if self._blit:
1722 err = RuntimeError('The animation function must return a sequence '
1723 'of Artist objects.')
Input In [53], in TKWindow.animate(self, i)
100 for ax in self.axes:
101 for artist in ax.lines + ax.collections:
--> 102 artist.remove()
103 if "someFunction" in self.dictWidgets:
104 ax=self.axes[0]
File ~\.virtualenvs\mdv-RxgQ3xiL\lib\site-packages\matplotlib\artist.py:211, in Artist.remove(self)
206 # There is no method to set the callback. Instead the parent should
207 # set the _remove_method attribute directly. This would be a
208 # protected attribute if Python supported that sort of thing. The
209 # callback has one parameter, which is the child to be removed.
210 if self._remove_method is not None:
--> 211 self._remove_method(self)
212 # clear stale callback
213 self.stale_callback = None
ValueError: list.remove(x): x not in list
Expected outcome
it should work like the first example
Additional information
python 3.10
Operating system
Windows 10
Matplotlib Version
3.5.2
Matplotlib Backend
module://matplotlib_inline.backend_inline
Python version
3.10
Jupyter version
3.4.0
Installation
No response