一次在tkinter中的两个动画

时间:2018-05-12 21:07:02

标签: python animation tkinter

我想让两个动画同时工作。

这是我的代码:

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import tkinter as tk

f = plt.figure(figsize=(6, 2), dpi=100)
f1 = plt.figure(figsize=(4, 2), dpi=100)

def animate(i):
    a = f.add_subplot(111)
    x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    y = [3, 5, 2, 7, 3, 5, 4, 6, 2, 2]
    a.plot(x, y)

def animate1(j):
    a1 = f1.add_subplot(111)
    x1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    y1 = [6, 1, 4, 9, 4, 2, 5, 1, 2, 4]
    a1.plot(x1, y1)

class Figure(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.config(background='blue')

        canvas = FigureCanvasTkAgg(f, self)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)

class Figure1(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.config(background='blue')

        canvas = FigureCanvasTkAgg(f1, self)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)


class Calculator(tk.Tk):
        def __init__(self, *args, **kwargs):
            tk.Tk.__init__(self, *args, *kwargs)
            tk.Tk.wm_title(self, 'Beam calculator')
            container = tk.Frame(self)
            container.pack(side='top', fill='both', expand=True)
            bfig = Figure(container, controller=self)
            bfig.grid(row=0, column=0, sticky="nsew")
            bfig1 = Figure(container, controller=self)
            bfig1.grid(row=1, column=0, sticky="nsew")


if __name__ == '__main__':

    app = Calculator()
    app.geometry('1280x720')
    ani = animation.FuncAnimation(f, animate, interval=1000)
    ani1 = animation.FuncAnimation(f1, animate1, interval=1000)
    app.mainloop()

但只出现第二个数字。

我找到了以下主题:

Animate two or more figures simultaneously with matplotlib

但我不知道如何正确使用它。

1 个答案:

答案 0 :(得分:0)

有三件小事妨碍您的代码工作:

  1. 使用grid时,您需要assign a positive weight to at least one row and one column

    class Calculator(tk.Tk):
        def __init__(self, *args, **kwargs):
            ...
            container.grid_columnconfigure(0, weight=1)
            # give the rows equal weight so they are allotted equal size
            container.grid_rowconfigure(0, weight=1)
            container.grid_rowconfigure(1, weight=1)
    
  2. bfig1 = Figure(container, controller=self)更改为bfig1 = Figure1(container, controller=self)。否则,画布永远不会附加到图f1

  3. *kwargs应该在**kwargs行中tk.Tk.__init__(self, *args, *kwargs)。 有关*args**kwargs的意思,请参阅this post for more

  4. 这些是使代码运行所需的最小更改。

    您可以使用许多其他更改来改进代码。

    一个重要的一点是a = f.add_subplot(111)不应放在animate函数内。这会在图中a创建一个Axes对象f。通常每个图有一个轴和一个图。 (请参阅matplotlib's hierarchy of objects。)由于animate会为动画的每个帧调用一次,因此您不希望为每个帧创建新的Axes。以下代码建议如何在全球范围内定义a一次。

    另一个主要的编程原则是首字母缩略词DRY: "Don't repeat youself"

    虽然首先复制代码可能很容易,但从长远来看,它可以进行维护 并更难修改代码。例如,假设您决定要添加 Figure类的一些代码。因为你有两个几乎相同的Figure 类,您可能需要向FigureFigure1添加相同的代码。和 如果您后来发现该代码中存在错误,则必须修复这两个错误 地方也是。由于每次编辑都需要完成,因此您的工作效率会降低一半 两次。如果你忘记在一个地方做,那么就会引入另一个bug。

    您可以通过推广animateFigure来使您的代码更干净 可以删除animate1Figure1。您需要做的就是添加一个额外的参数 功能(例如linef)以概括代码并允许代码 重用。 (请参阅下面的代码,了解我的意思。)

    使用带编号的变量名称几乎总是一个坏主意。 通常应使用listdict代替编号变量。 由于编号的变量名通常以相同的方式使用, (即对x1执行此操作,对x2执行相同操作,对x3执行相同操作,等等...) 使用编号变量导致代码重复违反DRY原则。 如果您使用列表来收集一个对象中的所有数字变量,那么您可以使用循环一次性处理它们:

    for x in xs:
        do_something(x)
    

    性能提示:在动画中调用plt.plot数千次可能会使您的程序显着缓慢。每次调用都会生成新的Line2D个对象,需要使用动画的每个帧重新渲染。为了获得更好的性能,一遍又一遍地重用相同的Line2D对象,只需更改Line2D对象中的ydata,以使matplotlib呈现不同的行:

    line.set_ydata(y)
    
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    import matplotlib.animation as animation
    import matplotlib.figure as mplfig
    import tkinter as tk
    
    def animate(i, line):
        y = np.random.randint(10, size=len(x))
        line.set_ydata(y)
        return [line]
    
    class Figure(tk.Frame):
    
        def __init__(self, parent, controller, f):
            tk.Frame.__init__(self, parent)
            self.controller = controller
            canvas = FigureCanvasTkAgg(f, self)
            canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
            canvas.draw()
    
    class Calculator(tk.Tk):
    
        def __init__(self, *args, **kwargs):
            tk.Tk.__init__(self, *args, **kwargs)
            self.wm_title('Beam calculator')
            container = tk.Frame(self)
            container.pack(side='top', fill='both', expand=True)
            for i, f in enumerate(figs):
                bfig = Figure(container, controller=self, f=f)
                bfig.grid(row=i, column=0, sticky="nsew")
                # give the rows equal weight so they are allotted equal size
                container.grid_rowconfigure(i, weight=1)
            # you need to give at least one row and one column a positive weight 
            # https://stackoverflow.com/a/36507919/190597 (Bryan Oakley)
            container.grid_columnconfigure(0, weight=1)
    
    if __name__ == '__main__':
        figs = [mplfig.Figure(figsize=(6, 2), dpi=100),
              mplfig.Figure(figsize=(4, 2), dpi=100)]
        axs = [f.add_subplot(111) for f in figs]
        x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        ys = [[3, 5, 2, 7, 3, 5, 4, 6, 2, 2],
              [6, 1, 4, 9, 4, 2, 5, 1, 2, 4]]
        lines = [ax.plot(x, ys[i])[0] for i, ax in enumerate(axs)]
        for ax in axs:
            ax.set_ylim([0, 10])
    
        app = Calculator()
        app.geometry('1280x720')
        anis = [animation.FuncAnimation(f, animate, interval=1000, fargs=[line])
                for f, line in zip(figs,lines)]
        app.mainloop()