Анимированный сюжет Matplotlib в Ткинтере - PullRequest
0 голосов
/ 09 апреля 2020

Я действительно борюсь с живым сюжетом, используя FuncAnimation и Tkinter. Я не уверен, какая информация необходима, чтобы воспроизвести проблему. Пожалуйста, прости меня, если чего-то не хватает. В урезанной версии он работает нормально:

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()

Но когда я интегрирую его в GUI с меню, он просто не вызывает функцию анимации. Кажется, когда я передаю свой объект Plot как вызов команды, он больше не работает? Если я вызываю функцию запуска напрямую, она теряет работу. Я понятия не имею, почему:

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()

Любая помощь будет принята с благодарностью! ;)

1 Ответ

0 голосов
/ 16 апреля 2020

Я нашел решение, хотя я не знаю, насколько он умен ... Теперь я просто непрерывно черчу xdata и ydata. Так как я передаю ссылку, в ней обновляются любые изменения вне класса графика. Тем не менее, я перерисовываю лунку каждый раз ...

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...