Skip to content

[Bug]: Can't clear artists when using HostAxes #24203

Closed
@Kreijstal

Description

@Kreijstal

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions