I am trying to embed an animation using FuncAnimation from matplotlib into a tkninter GUI. In the execute button at the bottom I am calling the Execute function. If I click the execute button many times the animations either get slower or plot over one another. The way it is written now, new plots are superposed. Previously, they would not superpose but would just get slower. In either case I get a warning that says I have plotted too many figures.
GOAL: I want an empty figure within my tkinter window. Then when the execute button is clicked I want the plots to be made. If it is clicked again, I want the previous plots to be cleared and new ones made with the new parameters. I suppose I should define the figure outside of Execute() and then add something like plt.clf() inside Execute(). This does not work.
import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import numpy as np import tkinter as tk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg root = tk.Tk() #labels entry widgets WidthLabel = tk.StringVar() WidthLabel.set("Enter width of well: ") tk.Label(root, textvariable=WidthLabel, height=2).grid(row=1, column=0) EnergyLabel = tk.StringVar() EnergyLabel.set("Enter energy level: ") tk.Label(root, textvariable=EnergyLabel, height=1).grid(row=2,column=0) #entry widgets WidthEntry = tk.Entry(root) WidthEntry.grid(row=1, column=1) EnergyLevelEntry = tk.Entry(root) EnergyLevelEntry.grid(row=2, column=1) fig = plt.figure() fig.subplots_adjust(hspace=0.3) # must initialize these variables to create plots. They will be changed by input # later. a = 5 C = 5 # making two plots IMAGINARY = plt.subplot(212) plt.xlim(0, a) plt.ylim(-1.35*C, 1.35*C) plt.yticks(fontsize=8) plt.text(a*.01, 1.1*C, 'Imaginary part') REAL = plt.subplot(211, sharex=IMAGINARY) plt.xlim(0, a) plt.ylim(-1.35*C, 1.35*C) plt.xticks(fontsize=8) plt.yticks(fontsize=8) plt.text(a*.01, 1.1*C, 'Real part') plt.setp(REAL.get_xticklabels(), visible=False) # This is for later animation. I do not fully understand what FuncAnimation does # but it is necessary to have these objects be iterable. line1, = IMAGINARY.plot(, , lw=2) line2, = REAL.plot(,, lw=2) #line3, = IMAGINARY.plot(, , lw=2) # defining function for later defined execute button to call def Execute(): # this is my attempt to clear the previous figure each time new data is given # in the entry widgets. It does not work and is the main source of my # confusion. plt.clf() # this is here so entering something that cannot be converted into a string # can eventually be communicated to the user without messing up the program. try: a = WidthEntry.get() a = float(a) n = EnergyLevelEntry.get() n = int(n) except ValueError: print("Bad Input") C = np.sqrt(2/a) # this sets up the axes and stuff based on the user input. plt.xlim(0, a) plt.ylim(-1.35*C, 1.35*C) plt.yticks(fontsize=8) plt.text(a*.01, 1.1*C, 'Imaginary part') plt.xlim(0, a) plt.ylim(-1.35*C, 1.35*C) plt.xticks(fontsize=8) plt.yticks(fontsize=8) plt.text(a*.01, 1.1*C, 'Real part') # this creates the x values over which y_vals will be calculated below. x = np.linspace(0, a, 500) def Real(x_vals,time): y_vals = np.cos(time)*C*np.sin(n*np.pi*x_vals/a) return y_vals def Imaginary(x_vals,time): y_vals = np.sin(time)*C*np.sin(n*np.pi*x_vals/a) return y_vals #animate functions will be used for animating. def animate(i): t = 0.01*i y = Real(x,t) line1.set_data(x,y) return line1, def animate2(i): t = 0.01*i y = Imaginary(x, t) line2.set_data(x,y) return line2, FigureCanvasTkAgg(fig, master=root).get_tk_widget().grid(row=0,column=0) FuncAnimation(fig, animate, frames = 50000, interval = 2, blit = True) FuncAnimation(fig, animate2, frames = 50000, interval = 2, blit = True) #FuncAnimation(fig, animate3, frames = 50000, interval = 2, blit = True) # this is where I make the execute button widget. ExecuteButton = tk.Button(root, text="Execute", command=Execute).grid(row=3, column=1) root.mainloop()