как использовать blit = true для анимации в tkinter python

Следующий код представляет собой упрощенную версию моего проекта, и он может запускаться и строиться с анимацией, но мне нужно добавить blit=true в анимацию для ускорения (мой исходный пользовательский интерфейс намного сложнее и медленнее). Я знаю, что функция анимации должна возвращать последовательность объектов Artist. вот что такое объект художника в моем subplot_2? Я пробовал subplot_2,a,f, ни один из них не работает. Большое спасибо

from multiprocessing import Process
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
##, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import tkinter as tk
from tkinter import ttk
from tkinter import *
import matplotlib.pyplot as plt
import numpy as np
f = Figure(figsize=(6,6), dpi=100)
subplot_1 = f.add_subplot(211)
subplot_2 = f.add_subplot(212)

LARGE_FONT= ("Verdana", 12)
style.use("ggplot")

def animate(i):
    time = np.random.rand(2, 25)  
    data = np.random.rand(2, 25)   
    a = subplot_2.scatter(time,data,c='blue',s=2)
    #return a

class home(tk.Tk):

def __init__(self, *args, **kwargs):        
    tk.Tk.__init__(self, *args, **kwargs)
    tk.Tk.wm_title(self, "Graph ")       
    self.geometry("1000x1000")

    container = tk.Frame(self)
    container.pack(side="top", fill="both", expand = True)
    container.grid_rowconfigure(0, weight=1)
    container.grid_columnconfigure(0, weight=1)

    self.frames = {}
    F=Graph
    frame=Graph(container, self)
    self.frames[F] = frame
    frame.grid(row=0, column=0, sticky="nsew")
    self.show_frame(Graph)

def show_frame(self, cont):
    frame = self.frames[cont]
    frame.tkraise()
def get_frame(self, frame_class):
    return self.frames[frame_class]
##     
class Graph(tk.Frame):
    def __init__(self, parent, controller):
        global canvas
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text=" ", font=LARGE_FONT)
        label.pack(pady=120,padx=10)
        collectbtn=Button(self,text='collect',command=self.clickstart)
        collectbtn.place(x=200,y=100)
        canvas = FigureCanvasTkAgg(f, self)
##        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
    def clickstart(self):
        animate(0)
        #aniplot_photon = animation.FuncAnimation(f, animate, blit=True,interval=100)
        aniplot_photon = animation.FuncAnimation(f, animate, interval=100)
        canvas.draw()
app = home()
app.mainloop()

Обновления: благодаря ответу Уильяма это работает. Однако у меня есть дополнительные вопросы, если этот подграфик включает много кривых "subplot_2.scatter(x1..),subplot_2.scatter(x2..),subplot_2.scatter(x3..),subplot_2.scatter(x1..)..." или некоторые подграфики обновляются из разных потоков (создавать объекты и глобальные их?), будет сложно поместить все кривые в список возврата. Тем не менее, я нашел простое решение для перечисленных выше проблем, я мог бы просто использовать return [subplot_2], и он обновит все графики внутри subplot_2, но координаты (x тиков) будут потеряны (запустите, и вы увидите). есть ли простой способ сохранить координаты, когда я использую return [suplot_2]?

    def animate(i):
    time = np.random.rand(2, 25)  
    data = np.random.rand(2, 25)   
    a = subplot_2.scatter(time,data,c='blue',s=2)
    return [subplot_2]

person adam wu    schedule 15.04.2020    source источник


Ответы (1)


matplotlib.pyplot.scatter возвращает исполнителя (точнее, PathCollection ), поэтому исполнитель, которого нужно вернуть из animate, хранится в a. Однако для FuncAnimation требуется последовательность исполнителей, поэтому

    return a

недостаточно (поскольку это один исполнитель, а не последовательность). Вы можете использовать один из

    return [a]

or

    return a,

для удовлетворения требования "последовательность исполнителей".

person William Miller    schedule 15.04.2020
comment
спасибо, в этом blit=true график истории не сохраняется, поэтому мне приходится рисовать по-разному, пытаться каждый раз сохранять данные в длинный список и каждый раз заново строить весь список? - person adam wu; 16.04.2020
comment
в моем исходном проекте проще вернуть [subplot_2], чем вернуть [a], поэтому мне не нужно указывать каждую кривую на subplot_2, однако таким образом я потерял информацию о координации на рисунке, любые идеи сохранить эти координаты на subplot_2? - person adam wu; 16.04.2020
comment
@adamwu В этом примере область subplot_2 ограничена таким образом, что вы все равно можете получить к нему доступ вне цикла, поэтому вы можете использовать matplotlib.axes.Axes.get_children() для доступа к отдельным PathCollection, созданным plt.scatter. - person William Miller; 16.04.2020
comment
Вы имеете в виду, что я использую return [subplot_2], а затем использую axes.get для повторного построения координат? не очень понимаю, несколько кодов строк будут полезны ^_^ - person adam wu; 16.04.2020
comment
@adamwu Вам не нужно возвращать subplot_2, чтобы получить доступ к детским исполнителям, вы можете просто сделать artists = subplot_2.get_children(), artists тогда будет список исполнителей, содержащий все PathCollection из subplot_2.scatter (среди прочего). В любой момент, когда вам нужно получить к ним доступ после вызова FuncAnimation, вы можете использовать этот подход. - person William Miller; 16.04.2020
comment
Я должен вернуть [subplot_2] или вернуть [a], так как анимация требует, чтобы вы что-то возвращали (кривые или разбросы, которые вы хотите обновить), чтобы соответствовать blit=True, что сделает анимацию быстрее - person adam wu; 16.04.2020
comment
@adamwu Я должен извиниться, я не совсем понимаю, о чем вы спрашиваете в своих комментариях. Возможно, вы захотите либо отредактировать свой текущий вопрос, либо открыть новый вопрос. - person William Miller; 16.04.2020