Matplotlib FuncAnimation 创建两次 - 嵌入到 tkinter 中时重复

Matplotlib FuncAnimation Created twice - duplicate when embbeded in tkinter

我有一个麻烦的错误,我无法理解它的来源。几天的尝试,仍然没有运气。

我正在尝试创建一个与使用 FuncAnimation 播放的音频相对应的线光标,出于某种原因,仅当激活该功能的回调 (line_select_callback) 时才会创建动画两次用鼠标绘制后从 RectangleSelector 小部件触发。当我使用标准 TK 按钮激活 SAME 功能 (line_select_callback) 时,它运行良好。

存在一些带有相关打印的调试代码。

我创建了最小的工作示例。 我的猜测是它与未附加到 tk window 的图形有关,并且除了嵌入的图形外还被静默激活,我不太确定。

非常感谢任何帮助,谢谢! :)

import os
import threading
import tkinter as tk


from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg)
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

from matplotlib import animation

class LineAnimation:
    def __init__(self,  fig, ax):
        print(' enter LineAnimation ctor')

        # Parameters
        self.ax = ax
        self.fig = fig
        self.xdata, self.ydata = [], []
        self.ln, = plt.plot([], [], 'ro')

        # Print figures list
        figures = [manager.canvas.figure
                   for manager in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
        print('figures BEFORE animation: ', figures)

        self.animation = animation.FuncAnimation(fig=self.fig,
                                                 func=self.update,
                                                 init_func=self.init,
                                                 frames=np.linspace(0, 2 * np.pi, 128),
                                                 interval=25,
                                                 blit=True, repeat=False,
                                                 cache_frame_data=False)

        self.fig.canvas.draw()
        # Print figures list
        figures = [manager.canvas.figure
                   for manager in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
        print('figures AFTER animation: ', figures, '\n')

    def init(self):
        # Prints for debugging
        print('\nenter init animate')
        print('Thread id: ', threading.get_ident())
        print('Process id: ', os.getpid(), '\n')


        # Init
        self.ax.set_xlim(0, 2*np.pi)
        self.ax.set_ylim(-1, 1)
        return self.ln,

    def update(self, frame):
        self.xdata.append(frame)
        self.ydata.append(np.sin(frame))
        self.ln.set_data(self.xdata, self.ydata)
        return self.ln,

class Example:

    def __init__(self):

        # init window
        self.root = tk.Tk(className=' Species segmentation')
        self.fig, self.ax = plt.subplots()

        # init sine audio file
        self.fs = 44100
        self.dur = 2
        self.freq = 440
        self.x = np.sin(2*np.pi*np.arange(self.fs*self.dur)*self.freq/self.fs)
        # plt.ion()

        # Embedd in tk
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)  # A tk.DrawingArea.
        self.canvas.draw()
        self.canvas.get_tk_widget().grid()

        # Plot something
        self.N = 100000
        self.xp = np.linspace(0, 10, self.N)
        self.ax.plot(self.xp, np.sin(2*np.pi*self.xp))
        self.ax.set_title(
            "Plot for demonstration purpuse")

        # init Rectangle Selector
        self.RS = RectangleSelector(self.ax, self.line_select_callback,
                                   drawtype='box', useblit=True,
                                   button=[1, 3],  # avoid using middle button
                                   minspanx=5, minspany=5,
                                   spancoords='pixels', interactive=True,
                                   rectprops={'facecolor': 'yellow', 'edgecolor': 'black', 'alpha': 0.15, 'fill': True})

        self.canvas.draw()
        # plt.show()


        tk.mainloop()

    def line_select_callback(self, eclick, erelease):
        print('enter line_select_callback')

        self.anim = LineAnimation(
            self.fig,
            self.ax)

        self.fig.canvas.draw()

        # plt.show()

Example()

我设法找出了这个问题的原因: 矩形选择器(使用 blitting)和在同一轴上使用动画(也使用 blitting)。 我设法正确地创建了动画,但只有当我禁用了矩形选择器时

    self.RS.set_active(False)
    self.RS.update()
    self.canvas.flush_events()

并删除了他的艺术家(我需要在我的代码中手动执行此操作)使用:

    for a in self.RS.artists:
        a.set_visible(False)

之后,动画正常运行。