我想让两个动画同时工作。
这是我的代码:
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
但我不知道如何正确使用它。
答案 0 :(得分:0)
有三件小事妨碍您的代码工作:
使用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)
将bfig1 = Figure(container, controller=self)
更改为bfig1 = Figure1(container, controller=self)
。否则,画布永远不会附加到图f1
。
*kwargs
应该在**kwargs
行中tk.Tk.__init__(self, *args, *kwargs)
。
有关*args
和**kwargs
的意思,请参阅this post for more。
这些是使代码运行所需的最小更改。
您可以使用许多其他更改来改进代码。
一个重要的一点是a = f.add_subplot(111)
不应放在animate
函数内。这会在图中a
创建一个Axes对象f
。通常每个图有一个轴和一个图。 (请参阅matplotlib's hierarchy of objects。)由于animate
会为动画的每个帧调用一次,因此您不希望为每个帧创建新的Axes。以下代码建议如何在全球范围内定义a
一次。
另一个主要的编程原则是首字母缩略词DRY: "Don't repeat youself"。
虽然首先复制代码可能很容易,但从长远来看,它可以进行维护
并更难修改代码。例如,假设您决定要添加
Figure
类的一些代码。因为你有两个几乎相同的Figure
类,您可能需要向Figure
和Figure1
添加相同的代码。和
如果您后来发现该代码中存在错误,则必须修复这两个错误
地方也是。由于每次编辑都需要完成,因此您的工作效率会降低一半
两次。如果你忘记在一个地方做,那么就会引入另一个bug。
您可以通过推广animate
和Figure
来使您的代码更干净
可以删除animate1
和Figure1
。您需要做的就是添加一个额外的参数
功能(例如line
和f
)以概括代码并允许代码
重用。 (请参阅下面的代码,了解我的意思。)
使用带编号的变量名称几乎总是一个坏主意。
通常应使用list
或dict
代替编号变量。
由于编号的变量名通常以相同的方式使用,
(即对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()