I have a troubleing bug that i just could not understands it’s origin. Several days of attempts and still no luck.
I’m trying to create a line cursor that correspond to played audio with FuncAnimation
and for some reason, the animation is created twice ONLY when the callback (line_select_callback
) that activates the function is triggered from RectangleSelector
widget after drawing wiith the mouse. when I use a standard TK
button to activate the SAME function (line_select_callback
), it operates well.
some debugging code with reevant prints is present.
I’ve created minimal working example. My guess is it has something to do with the figure that is not attached to the tk window, and is silently activated in addition to the embedded figure, I’m not really sure.
Any help will be very much appreciated, Thanks! :)
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()
Advertisement
Answer
I managed to isolate the cause for this issue: The presence of the rectangle selector (which uses blitting) and the use of animation (which also uses blitting) on the same axes. I’ve managed to create the animation properly, but only when I disabled the rectangle selector
self.RS.set_active(False) self.RS.update() self.canvas.flush_events()
and removed his artists (i needed to do that manually in my code) using:
for a in self.RS.artists: a.set_visible(False)
after that, The animation worked properly.