Python 为什么动画在恢复后会加速?
我正在制作tkinter的单摆动画。我已将s和r绑定到窗口以停止并恢复动画。但我注意到了一个我无法解释的奇怪现象:如果我在钟摆没有停止时按r,它就会加速。我不明白为什么会这样。有人能解释发生了什么以及如何解决吗 这是我的密码:Python 为什么动画在恢复后会加速?,python,python-3.x,animation,tkinter,Python,Python 3.x,Animation,Tkinter,我正在制作tkinter的单摆动画。我已将s和r绑定到窗口以停止并恢复动画。但我注意到了一个我无法解释的奇怪现象:如果我在钟摆没有停止时按r,它就会加速。我不明白为什么会这样。有人能解释发生了什么以及如何解决吗 这是我的密码: from math import sqrt, cos, sin, radians from tkinter import * class SimplePendulum: def __init__(self): # Create a window
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum:
def __init__(self):
# Create a window
win = Tk()
win.title('Pendulum')
# Create a canvas
self.w, self.h = 250, 300
self.canvas = Canvas(win, width=self.w, height=self.h, bg='white')
self.canvas.pack()
# Bind keys to the window
win.bind('s', self.stop)
win.bind('S', self.stop)
win.bind('r', self.resume)
win.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.L = 4*self.h/5
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
# Start animation
self.isStopped = False
self.speed = 1/50 # initial frequency of oscillation
self.animate()
# Start the event loop
win.mainloop()
def drawPendulum(self):
# Angle of the pendulum (from the vertial) at time t
theta = self.theta_i * cos(sqrt(self.g/self.L) * self.t)
# The two ends of the cord
x_i, y_i = self.w/2, self.h/10
x_f, y_f = x_i + self.L*sin(theta), y_i + self.L*cos(theta)
# Draw the cord and bob of the pendulum
self.canvas.create_line(x_i, y_i, x_f, y_f, tags='cord')
rad = min(self.w, self.h)/20
self.canvas.create_oval(x_f - rad, y_f - rad,
x_f + rad, y_f + rad, fill='red', tags='bob')
def animate(self):
if not self.isStopped:
self.canvas.delete(ALL)
self.drawPendulum()
self.t += 2
self.canvas.after(int(1/self.speed), self.animate)
def stop(self, event):
self.isStopped = True
def resume(self, event):
self.isStopped = False
self.animate()
SimplePendulum()
只需将resume替换为:
如果在不检查self.isStopped标志的情况下运行resume,则它会运行一个新的动画,该动画有自己的调用递归,因此每次调用resume时,它都会调用线性递增的动画方法,而不检查标志。只需将resume替换为:
当运行resume时不带if self.isStopped标志检查,它会运行一个新的动画,该动画有自己的调用递归,因此每次调用resume时,它都会调用线性递增的动画方法,而不带标志。或者,您可以使用after\u cancel取消动画队列。但不确定这是否有好处:
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum:
def __init__(self):
# Create a window
win = Tk()
win.title('Pendulum')
# Create a canvas
self.w, self.h = 250, 300
self.canvas = Canvas(win, width=self.w, height=self.h, bg='white')
self.canvas.pack()
# Bind keys to the window
win.bind('s', self.stop)
win.bind('S', self.stop)
win.bind('r', self.resume)
win.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.L = 4*self.h/5
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
self._queue = False
# Start animation
self.isStopped = False
self.speed = 1/50 # initial frequency of oscillation
self.animate()
# Start the event loop
win.mainloop()
def drawPendulum(self):
# Angle of the pendulum (from the vertial) at time t
theta = self.theta_i * cos(sqrt(self.g/self.L) * self.t)
# The two ends of the cord
x_i, y_i = self.w/2, self.h/10
x_f, y_f = x_i + self.L*sin(theta), y_i + self.L*cos(theta)
# Draw the cord and bob of the pendulum
self.canvas.create_line(x_i, y_i, x_f, y_f, tags='cord')
rad = min(self.w, self.h)/20
self.canvas.create_oval(x_f - rad, y_f - rad,
x_f + rad, y_f + rad, fill='red', tags='bob')
def animate(self):
self.canvas.delete(ALL)
self.drawPendulum()
self.t += 2
self._queue = self.canvas.after(int(1/self.speed), self.animate)
def stop(self, event):
if self._queue:
self.canvas.after_cancel(self._queue)
self._queue = False
def resume(self, event):
if not self._queue:
self.animate()
SimplePendulum()
或者,可以使用“取消后”来取消动画队列。但不确定这是否有好处:
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum:
def __init__(self):
# Create a window
win = Tk()
win.title('Pendulum')
# Create a canvas
self.w, self.h = 250, 300
self.canvas = Canvas(win, width=self.w, height=self.h, bg='white')
self.canvas.pack()
# Bind keys to the window
win.bind('s', self.stop)
win.bind('S', self.stop)
win.bind('r', self.resume)
win.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.L = 4*self.h/5
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
self._queue = False
# Start animation
self.isStopped = False
self.speed = 1/50 # initial frequency of oscillation
self.animate()
# Start the event loop
win.mainloop()
def drawPendulum(self):
# Angle of the pendulum (from the vertial) at time t
theta = self.theta_i * cos(sqrt(self.g/self.L) * self.t)
# The two ends of the cord
x_i, y_i = self.w/2, self.h/10
x_f, y_f = x_i + self.L*sin(theta), y_i + self.L*cos(theta)
# Draw the cord and bob of the pendulum
self.canvas.create_line(x_i, y_i, x_f, y_f, tags='cord')
rad = min(self.w, self.h)/20
self.canvas.create_oval(x_f - rad, y_f - rad,
x_f + rad, y_f + rad, fill='red', tags='bob')
def animate(self):
self.canvas.delete(ALL)
self.drawPendulum()
self.t += 2
self._queue = self.canvas.after(int(1/self.speed), self.animate)
def stop(self, event):
if self._queue:
self.canvas.after_cancel(self._queue)
self._queue = False
def resume(self, event):
if not self._queue:
self.animate()
SimplePendulum()
与您的问题不太相关,但如果您更新画布上的元素,而不是清除整个画布并从头开始重新绘制,则动画会更平滑:
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum:
def __init__(self):
# Create a window
win = Tk()
win.title('Pendulum')
# Create a canvas
self.w, self.h = 250, 300
self.canvas = Canvas(win, width=self.w, height=self.h, bg='white')
self.canvas.pack()
# Bind keys to the window
win.bind('s', self.stop)
win.bind('S', self.stop)
win.bind('r', self.resume)
win.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.L = 4*self.h/5
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
cord, bob = self.calcPendulum()
self.cord = self.canvas.create_line(*cord, tags='cord')
self.bob = self.canvas.create_oval(*bob, fill='red', tags='bob')
# Start animation
self.isStopped = False
self.speed = 1/50 # initial frequency of oscillation
self.animate()
# Start the event loop
win.mainloop()
def calcPendulum(self):
# Angle of the pendulum (from the vertial) at time t
theta = self.theta_i * cos(sqrt(self.g/self.L) * self.t)
# The two ends of the cord
x_i, y_i = self.w/2, self.h/10
x_f, y_f = x_i + self.L*sin(theta), y_i + self.L*cos(theta)
rad = min(self.w, self.h)/20
cord_pos = x_i, y_i, x_f, y_f
bob_pos = x_f - rad, y_f - rad, x_f + rad, y_f + rad
return cord_pos, bob_pos
def animate(self):
if not self.isStopped:
cord, bob = self.calcPendulum()
self.canvas.coords(self.cord, *cord)
self.canvas.coords(self.bob, *bob)
self.t += 2
self.canvas.after(int(1/self.speed), self.animate)
def stop(self, event):
self.isStopped = True
def resume(self, event):
self.isStopped = False
self.animate()
SimplePendulum()
而且,在这里上课也没有意义。我怀疑你这样做是因为你看到或被告知GUI中的所有东西都需要一个类。但关键是它需要是GUI小部件的子类。例如,您可以使SimplePendulum类成为画布的一种类型:
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum(Canvas):
def __init__(self, master=None, **kwargs):
Canvas.__init__(self, master, bg='white', **kwargs)
# Bind keys to the window
master.bind('s', self.stop)
master.bind('S', self.stop)
master.bind('r', self.resume)
master.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
cord, bob = self.calcPendulum()
self.cord = self.create_line(*cord, tags='cord')
self.bob = self.create_oval(*bob, fill='red', tags='bob')
# Start animation
self.timer = ''
self.speed = 1/50 # initial frequency of oscillation
self.animate()
def calcPendulum(self):
# Angle of the pendulum (from the vertial) at time t
L = 4*self.winfo_height()/5
theta = self.theta_i * cos(sqrt(self.g/L) * self.t)
# The two ends of the cord
x_i, y_i = self.winfo_width()/2, self.winfo_height()/10
x_f, y_f = x_i + L*sin(theta), y_i + L*cos(theta)
rad = min(self.winfo_width(), self.winfo_height())/20
cord_pos = x_i, y_i, x_f, y_f
bob_pos = x_f - rad, y_f - rad, x_f + rad, y_f + rad
return cord_pos, bob_pos
def animate(self):
cord, bob = self.calcPendulum()
self.coords(self.cord, *cord)
self.coords(self.bob, *bob)
self.t += 2
self.timer = self.after(int(1/self.speed), self.animate)
def stop(self, event=None):
self.after_cancel(self.timer)
def resume(self, event=None):
self.stop() # in case it's currently running, stop it
self.animate()
def main():
# Create a window
win = Tk()
win.title('Pendulum')
part = SimplePendulum(win, width=200, height=300)
part.pack(fill=BOTH, expand=True)
win.mainloop() # Start the event loop
if __name__ == '__main__':
main()
现在你可以像我一样在一个小演示程序中使用你的新部件,或者将它打包到任何更大的程序中。或者多次使用它
我还移动了其他一些东西,比如将长度计算放在计时步骤中,这样您就可以调整窗口的大小,钟摆也可以调整大小。你的数学很有趣,因为它很好地演示了钟摆长度和频率之间的关系。嗯,现在我们有了一个整洁的小部件,我们可以通过在屏幕上放一个短的和一个高的来轻松演示:
def main():
# Create a window
win = Tk()
win.title('Pendulum')
part = SimplePendulum(win, width=200, height=100)
part.pack(side=LEFT)
part = SimplePendulum(win, width=400, height=600)
part.pack(side=LEFT)
win.mainloop() # Start the event loop
与您的问题不太相关,但如果您更新画布上的元素,而不是清除整个画布并从头开始重新绘制,则动画会更平滑:
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum:
def __init__(self):
# Create a window
win = Tk()
win.title('Pendulum')
# Create a canvas
self.w, self.h = 250, 300
self.canvas = Canvas(win, width=self.w, height=self.h, bg='white')
self.canvas.pack()
# Bind keys to the window
win.bind('s', self.stop)
win.bind('S', self.stop)
win.bind('r', self.resume)
win.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.L = 4*self.h/5
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
cord, bob = self.calcPendulum()
self.cord = self.canvas.create_line(*cord, tags='cord')
self.bob = self.canvas.create_oval(*bob, fill='red', tags='bob')
# Start animation
self.isStopped = False
self.speed = 1/50 # initial frequency of oscillation
self.animate()
# Start the event loop
win.mainloop()
def calcPendulum(self):
# Angle of the pendulum (from the vertial) at time t
theta = self.theta_i * cos(sqrt(self.g/self.L) * self.t)
# The two ends of the cord
x_i, y_i = self.w/2, self.h/10
x_f, y_f = x_i + self.L*sin(theta), y_i + self.L*cos(theta)
rad = min(self.w, self.h)/20
cord_pos = x_i, y_i, x_f, y_f
bob_pos = x_f - rad, y_f - rad, x_f + rad, y_f + rad
return cord_pos, bob_pos
def animate(self):
if not self.isStopped:
cord, bob = self.calcPendulum()
self.canvas.coords(self.cord, *cord)
self.canvas.coords(self.bob, *bob)
self.t += 2
self.canvas.after(int(1/self.speed), self.animate)
def stop(self, event):
self.isStopped = True
def resume(self, event):
self.isStopped = False
self.animate()
SimplePendulum()
而且,在这里上课也没有意义。我怀疑你这样做是因为你看到或被告知GUI中的所有东西都需要一个类。但关键是它需要是GUI小部件的子类。例如,您可以使SimplePendulum类成为画布的一种类型:
from math import sqrt, cos, sin, radians
from tkinter import *
class SimplePendulum(Canvas):
def __init__(self, master=None, **kwargs):
Canvas.__init__(self, master, bg='white', **kwargs)
# Bind keys to the window
master.bind('s', self.stop)
master.bind('S', self.stop)
master.bind('r', self.resume)
master.bind('R', self.resume)
# Pendulum constants
self.g = 1
self.theta_i = radians(20)
# Initialize time t to 0
self.t = 0
cord, bob = self.calcPendulum()
self.cord = self.create_line(*cord, tags='cord')
self.bob = self.create_oval(*bob, fill='red', tags='bob')
# Start animation
self.timer = ''
self.speed = 1/50 # initial frequency of oscillation
self.animate()
def calcPendulum(self):
# Angle of the pendulum (from the vertial) at time t
L = 4*self.winfo_height()/5
theta = self.theta_i * cos(sqrt(self.g/L) * self.t)
# The two ends of the cord
x_i, y_i = self.winfo_width()/2, self.winfo_height()/10
x_f, y_f = x_i + L*sin(theta), y_i + L*cos(theta)
rad = min(self.winfo_width(), self.winfo_height())/20
cord_pos = x_i, y_i, x_f, y_f
bob_pos = x_f - rad, y_f - rad, x_f + rad, y_f + rad
return cord_pos, bob_pos
def animate(self):
cord, bob = self.calcPendulum()
self.coords(self.cord, *cord)
self.coords(self.bob, *bob)
self.t += 2
self.timer = self.after(int(1/self.speed), self.animate)
def stop(self, event=None):
self.after_cancel(self.timer)
def resume(self, event=None):
self.stop() # in case it's currently running, stop it
self.animate()
def main():
# Create a window
win = Tk()
win.title('Pendulum')
part = SimplePendulum(win, width=200, height=300)
part.pack(fill=BOTH, expand=True)
win.mainloop() # Start the event loop
if __name__ == '__main__':
main()
现在你可以像我一样在一个小演示程序中使用你的新部件,或者将它打包到任何更大的程序中。或者多次使用它
我还移动了其他一些东西,比如将长度计算放在计时步骤中,这样您就可以调整窗口的大小,钟摆也可以调整大小。你的数学很有趣,因为它很好地演示了钟摆长度和频率之间的关系。嗯,现在我们有了一个整洁的小部件,我们可以通过在屏幕上放一个短的和一个高的来轻松演示:
def main():
# Create a window
win = Tk()
win.title('Pendulum')
part = SimplePendulum(win, width=200, height=100)
part.pack(side=LEFT)
part = SimplePendulum(win, width=400, height=600)
part.pack(side=LEFT)
win.mainloop() # Start the event loop
哦我懂了。谢谢你的帮助!哦我懂了。谢谢你的帮助!令人惊叹的谢谢你的改进!令人惊叹的谢谢你的改进!