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()
CodePudding user response:
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.