[英]Animated Matplotlib Plot in Tkinter
I'm really struggling with an live Plot using FuncAnimation and Tkinter.我真的很挣扎使用 FuncAnimation 和 Tkinter 的实时绘图。 I'm not sure what info is needed to reproduce the problem.我不确定重现问题需要什么信息。 Please forgive me if anything is missing.如有遗漏请见谅。 In a stripped down version it works fine:在精简版中,它工作正常:
from numpy.random import normal
from numpy import exp
def fn(x):
A=3e-8
B=6e3
Inoise = 1e-10
if x == 0:
I = Inoise
elif A*x**2*exp(-B/x) < Inoise:
I = Inoise
else:
I = A*x**2*exp(-B/x)
return normal(1,0.4,1)[0]*I
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
from matplotlib.animation import FuncAnimation
from matplotlib.pyplot import Figure
from tkinter import Frame
from queue import Queue
class LivePlotWindow(Frame):
def __init__(self, master,xlabel='',ylabel='',logx=False,logy=False):
Frame.__init__(self, master)
self.pack(fill='both', expand=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.toolbar = NavigationToolbar2Tk(self.canvas, self)
self.toolbar.update()
self.canvas.get_tk_widget().pack()
self.xlabel = xlabel
self.ylabel = ylabel
self.logx = logx
self.logy = logy
self.q = Queue()
self._axini()
def _axini(self):
self.ax.cla()
self.ax.set_xlabel(self.xlabel)
self.ax.set_ylabel(self.ylabel)
if self.logx==True:
self.ax.set_xscale('log', nonposx='clip')
if self.logy==True:
self.ax.set_yscale('log', nonposy='clip')
self.fig.tight_layout()
def _update(self,i):
print(i)
[x,y] = self.q.get(block=True)
print([x,y])
self.ax.plot(x,y,'b.') # update the data
def run(self,ncalls):
self._axini()
print(ncalls)
self.ani = FuncAnimation(self.fig, self._update, range(ncalls-1), interval=100, repeat=False)
print(ncalls)
def addP(self,x,y):
self.q.put([x,y])
def stop(self):
self.ani.event_source.stop()
from tkinter import Tk
root = Tk()
root.title('Kennlinien-Messprogramm')
root.geometry('700x500')
plot = LivePlotWindow(root,'Voltage in V','Current in A',logy=True)
n=100
plot.run(n)
for i in range(n):
plot.addP(i*10,fn(i*10))
root.mainloop()
But when I'm integrating it in a GUI with menu, its just not calling the animation function.但是当我将它集成到带有菜单的 GUI 中时,它只是不调用动画函数。 It seems when I pass my Plot object as a command call its not working anymore?似乎当我将 Plot 对象作为命令调用传递时,它不再工作了? If I call the start function directly it wortks.如果我直接调用 start 函数,它会起作用。 I have no idea why:我不知道为什么:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
from matplotlib.animation import FuncAnimation
from matplotlib.pyplot import Figure
from tkinter import Tk,Frame,Menu
from queue import Queue
class LivePlotWindow(Frame):
def __init__(self, master,xlabel='',ylabel='',logx=False,logy=False):
Frame.__init__(self, master)
self.pack(fill='both', expand=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.toolbar = NavigationToolbar2Tk(self.canvas, self)
self.toolbar.update()
self.canvas.get_tk_widget().pack()
self.xlabel = xlabel
self.ylabel = ylabel
self.logx = logx
self.logy = logy
self.q = Queue()
self._axini()
def _axini(self):
self.ax.cla()
self.ax.set_xlabel(self.xlabel)
self.ax.set_ylabel(self.ylabel)
if self.logx==True:
self.ax.set_xscale('log', nonposx='clip')
if self.logy==True:
self.ax.set_yscale('log', nonposy='clip')
self.fig.tight_layout()
def _update(self,i):
print(i)
[x,y] = self.q.get(block=True)
print([x,y])
self.ax.plot(x,y,'b.') # update the data
def run(self,ncalls):
self._axini()
print(ncalls)
self.ani = FuncAnimation(self.fig, self._update, range(ncalls-1), interval=100, repeat=False)
print(ncalls)
def addP(self,x,y):
self.q.put([x,y])
def stop(self):
self.ani.event_source.stop()
from tkinter import Tk
root = Tk()
root.title('Kennlinien-Messprogramm')
root.geometry('700x500')
plot = LivePlotWindow(root,'Voltage in V','Current in A',logy=True)
def start(menu,plot):
menu.entryconfig('\u25B6', state='disabled')
menu.entryconfig('\u25A0', state='normal')
n=100
plot.run(n)
for i in range(n):
plot.addP(i*10,fn(i*10))
def stop(menu,plot):
plot.stop
menu.entryconfig('\u25B6', state='normal')
menu.entryconfig('\u25A0', state='disabled')
menu = Menu(root)
root.config(menu=menu)
menu.add_command(label='\u25B6', command=lambda: start(menu,plot))
#menu.entryconfig(1, fg='green')
menu.add_command(label='\u25A0', command=lambda: stop(menu,plot))
menu.entryconfig('\u25A0', state='disabled')
#start(menu,plot)
root.mainloop()
Any help would be highly appreciated!任何帮助将不胜感激! ;) ;)
I've found a solution, although I don't know how smart it is... Now I just continously plot xdata and ydata.我找到了一个解决方案,虽然我不知道它有多聪明......现在我只是不断地绘制 xdata 和 ydata。 Since I pass the reference, its updating any changes outside the plot class.由于我传递了引用,它会更新绘图类之外的任何更改。 However, I redraw the hole plot every time...但是,我每次都重新绘制孔图......
from tkinter import Frame
from matplotlib.pyplot import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.animation import FuncAnimation
class LivePlotWindow(Frame):
def __init__(self, master, xdata, ydata, xlabel='',ylabel='',logx=False,logy=False):
Frame.__init__(self, master)
self.pack(fill='both', expand=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.ax.set_xlabel(xlabel)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.get_tk_widget().pack()
self.xdata = xdata
self.ydata = ydata
self.xlabel = xlabel
self.ylabel = ylabel
self.logx = logx
self.logy = logy
self.ani = FuncAnimation(self.fig, self._update, interval=500)
def _update(self,i):
self.ax.cla()
self.ax.set_xlabel(self.xlabel)
self.ax.set_ylabel(self.ylabel)
if self.logx:
self.ax.set_xscale('log', nonposx='clip')
if self.logy:
self.ax.set_yscale('log', nonposy='clip')
#if self.millikan:
# self.ax.plot(1/self.xdata,self.ydata)
#else:
self.ax.plot(self.xdata,self.ydata,'b.') # update the data
self.fig.tight_layout()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.