(Python 3.9 on Big Sur.) I have sequence of 100 square, 6-by-6 random matrices stored in the list M_list. I wish to create an animation of the corresponding heatmaps that appears on a tkinter canvas, along with an animation player.
I modified the approach at
Managing dynamic plotting in matplotlib Animation module
to get something that works (almost). The animation appears on the canvas along with five buttons (stop, forward play, backward play, forward single frame, backward single frame). While the forward/backward single frame buttons work fine, the forward/backward play buttons do not. Nothing happens when I click on either, and there is no error message. Looking at how the functions are defined, I don't see the source of the error. Here's my code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import mpl_toolkits.axes_grid1
import matplotlib.widgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import Tk,TOP,BOTH
class Player(FuncAnimation):
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
save_count=None,
mini=0, maxi=100, pos=(0.125, 0.92), **kwargs):
self.i = 0
self.min=mini
self.max=maxi
self.runs = True
self.forwards = True
self.fig = fig
self.func = func
self.setup(pos)
FuncAnimation.__init__(self,self.fig, self.func, frames=self.play(),
init_func=init_func, fargs=fargs,
save_count=save_count, **kwargs )
# Player class functions
def play(self):
while self.runs:
self.i = self.i+self.forwards-(not self.forwards)
if self.i > self.min and self.i < self.max:
yield self.i
else:
self.stop()
yield self.i
def start(self):
self.runs=True
self.event_source.start()
def stop(self, event=None):
self.runs = False
self.event_source.stop()
def forward(self, event=None):
self.forwards = True
self.start()
def backward(self, event=None):
self.forwards = False
self.start()
def oneforward(self, event=None):
self.forwards = True
self.onestep()
def onebackward(self, event=None):
self.forwards = False
self.onestep()
def onestep(self):
if self.i > self.min and self.i < self.max:
self.i = self.i+self.forwards-(not self.forwards)
elif self.i == self.min and self.forwards:
self.i+=1
elif self.i == self.max and not self.forwards:
self.i-=1
self.func(self.i)
self.fig.canvas.draw_idle()
# Buttons
def setup(self, pos):
playerax = self.fig.add_axes([pos[0],pos[1], 0.22, 0.04])
divider = mpl_toolkits.axes_grid1.make_axes_locatable(playerax)
bax = divider.append_axes("right", size="80%", pad=0.05)
sax = divider.append_axes("right", size="80%", pad=0.05)
fax = divider.append_axes("right", size="80%", pad=0.05)
ofax = divider.append_axes("right", size="100%", pad=0.05)
self.button_oneback = matplotlib.widgets.Button(playerax , label=u'$\u29CF$')
self.button_back = matplotlib.widgets.Button(bax, label=u'$\u25C0$')
self.button_stop = matplotlib.widgets.Button(sax, label=u'$\u25A0$')
self.button_forward = matplotlib.widgets.Button(fax, label=u'$\u25B6$')
self.button_oneforward = matplotlib.widgets.Button(ofax, label=u'$\u29D0$')
self.button_oneback.on_clicked(self.onebackward)
self.button_back.on_clicked(self.backward)
self.button_stop.on_clicked(self.stop)
self.button_forward.on_clicked(self.forward)
self.button_oneforward.on_clicked(self.oneforward)
# Create matrices
num_times=100
M_list=[]
for t in range(num_times):
M_list.append(np.random.rand(6,6))
fig=plt.Figure()
def update(i):
ax=fig.add_subplot(111)
ax.imshow(M_list[i],aspect='auto',cmap='jet')
fig.suptitle('Frame: '+str(i), fontsize=16)
ani = Player(fig, update, maxi=num_times) #len(y)-1)
# tkinter root window and canvas
root=Tk()
root.geometry('1000x1000')
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=TOP,fill=BOTH,expand=1)
root.mainloop()