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)
之后,动画正常运行。
我有一个麻烦的错误,我无法理解它的来源。几天的尝试,仍然没有运气。
我正在尝试创建一个与使用 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)
之后,动画正常运行。