使用python tkinter对双摆动画的奇特减慢效果

时间:2018-08-07 08:19:15

标签: python animation tkinter pendulum

我刚开始使用python,并尝试使用tkinter创建双摆动画。我知道,由于我的经验不足,我可能会采用复杂的方法。

我有一个我没有想到的错误。似乎摆锤在某些地方变慢了。随着时间的流逝,系统的能量似乎下降了,这是不应该发生的,因为我没有考虑任何摩擦。

我不认为我在theta1_dotdot和theta2_dotdot的公式中犯了一个错误,因为即使我使用了更简单的(非物理的)公式,速度也会降低。

这是我的程序:

from tkinter import*
from random import*
from math import*

gui = Tk()
gui.title("Double Pendulum")
canvas = Canvas(gui, width=300, height=300)
canvas.pack()

r1,r2,m1,m2 = 75,75,10,10
g = 9.81
t=0
delt=0.001
theta1 = random()*2*pi
theta2 = random()*2*pi
theta1_dot,theta2_dot = 0,0
dt = 0.1
t = 0

while t < 1000000:
    num1 = (-g*(2*m1+m2)*sin(theta1))
    num2 = -m2*g*sin(theta1-2*theta2)
    num3 = (-2*sin(theta1-theta2)*m2*            (theta2_dot**2*r2+theta1_dot**2*r1*cos(theta1-theta2)))
    denom1 = r1*(2*m1+m2-m2*cos(2*theta1-2*theta2))
    theta1_dotdot = (num1 + num2 + num3)/denom1

    num4 = 2*sin(theta1-theta2)
    num5 = (theta1_dot**2*r1*(m1+m2))
    num6 = g*(m1+m2)*cos(theta1)
    num7 = theta2_dot**2*r2*m2*cos(theta1-theta2)
    denom2 = r2*(2*m1+m2-m2*cos(2*theta1-2*theta2))
    theta2_dotdot = (num4*(num5+num6+num7))/denom2

    theta1_dot += theta1_dotdot * dt
    theta2_dot += theta2_dotdot * dt
    theta1 += theta1_dot * dt
    theta2 += theta2_dot * dt

    x1 = r1*sin(theta1)
    y1 = r1*cos(theta1)

    x2 = x1 + r2*sin(theta2)
    y2 = y1 + r2*cos(theta2)

    trace = canvas.create_oval(150 + x2, 60 + y2, 150 + x2, 60 + y2, fill='black', outline='black')
    lin1 = canvas.create_line(150,60,150+x1, 60+y1,width=3,fill='pink')
    lin2 = canvas.create_line(150+x1, 60+y1,150+x2, 60+y2,width=3,fill='pink')
    ov1 = canvas.create_oval(140+x1,50+y1,160+x1,70+y1,     fill='pink',outline='pink')
    ov2 = canvas.create_oval(140+x2,50+y2,160+x2,70+y2, fill='pink',outline='pink')
    t += .1
    canvas.after(1)
    canvas.update()
    canvas.delete(ov1)
    canvas.delete(ov2)
    canvas.delete(lin1)
    canvas.delete(lin2)

gui.mainloop()

1 个答案:

答案 0 :(得分:2)

我已经重写了您的代码,以删除通用导入(import *),使用类结构而不是全局变量和函数,并正确地实际使用tkinter mainloop和after函数:

import tkinter as tk
import random
from math import pi, sin, cos

r1,r2,m1,m2 = 75,75,10,10
g = 9.81

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Double Pendulum")
        self.canvas = tk.Canvas(self, width=300, height=300)
        self.canvas.pack()
        self.delt=0.001
        self.theta1 = random.random()*2*pi
        self.theta2 = random.random()*2*pi
        self.theta1_dot,self.theta2_dot = 0,0
        self.dt = 0.1
        self.t = 0

        self.after(1, self.do_after)

    def do_after(self):
        self.canvas.delete('pendulum')

        num1 = (-g*(2*m1+m2)*sin(self.theta1))
        num2 = -m2*g*sin(self.theta1-2*self.theta2)
        num3 = (-2*sin(self.theta1-self.theta2)*m2*(self.theta2_dot**2*r2+self.theta1_dot**2*r1*cos(self.theta1-self.theta2)))
        denom1 = r1*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta1_dotdot = (num1 + num2 + num3)/denom1

        num4 = 2*sin(self.theta1-self.theta2)
        num5 = (self.theta1_dot**2*r1*(m1+m2))
        num6 = g*(m1+m2)*cos(self.theta1)
        num7 = self.theta2_dot**2*r2*m2*cos(self.theta1-self.theta2)
        denom2 = r2*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta2_dotdot = (num4*(num5+num6+num7))/denom2

        self.theta1_dot += theta1_dotdot * self.dt
        self.theta2_dot += theta2_dotdot * self.dt
        self.theta1 += self.theta1_dot * self.dt
        self.theta2 += self.theta2_dot * self.dt

        x1 = r1*sin(self.theta1)
        y1 = r1*cos(self.theta1)

        x2 = x1 + r2*sin(self.theta2)
        y2 = y1 + r2*cos(self.theta2)

        self.canvas.create_oval(150 + x2, 60 + y2, 150 + x2, 60 + y2, fill='black', outline='black', tag='trace')
        self.canvas.create_line(150,60,150+x1, 60+y1,width=3,fill='pink', tags='pendulum')
        self.canvas.create_line(150+x1, 60+y1,150+x2, 60+y2,width=3,fill='pink', tags='pendulum')
        self.canvas.create_oval(140+x1,50+y1,160+x1,70+y1,     fill='pink',outline='pink', tags='pendulum')
        self.canvas.create_oval(140+x2,50+y2,160+x2,70+y2, fill='pink',outline='pink', tags='pendulum')
        self.t += .1

        self.after(1, self.do_after)

if __name__ == '__main__':
    app = App()
    app.mainloop()

在您的代码中,您需要在循环的每次迭代中强制调用update,而不是让tkinter在需要更新时进行处理,而是调用after而不使用AFAICT没有提供的回调其实什么也没做。
我还为钟摆部件添加了标签,因此您可以通过一次调用将其全部删除,而不必每次都存储其ID。

进一步测试真正的问题是,您正在画布上创建数千个tkinter难以渲染的对象。要保持跟踪,您可以保留坐标列表并将其绘制为一条线:

import tkinter as tk
import random
from math import pi, sin, cos

r1,r2,m1,m2 = 75,75,10,10
g = 9.81

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("Double Pendulum")
        self.canvas = tk.Canvas(self, width=300, height=300)
        self.canvas.pack()
        self.delt=0.001
        self.theta1 = random.random()*2*pi
        self.theta2 = random.random()*2*pi
        self.theta1_dot,self.theta2_dot = 0,0
        self.dt = 0.1
        self.t = 0

        self.trace_coords = []

        self.after(1, self.do_after)

    def do_after(self):
        self.canvas.delete('trace')
        self.canvas.delete('pendulum')

        num1 = (-g*(2*m1+m2)*sin(self.theta1))
        num2 = -m2*g*sin(self.theta1-2*self.theta2)
        num3 = (-2*sin(self.theta1-self.theta2)*m2*(self.theta2_dot**2*r2+self.theta1_dot**2*r1*cos(self.theta1-self.theta2)))
        denom1 = r1*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta1_dotdot = (num1 + num2 + num3)/denom1

        num4 = 2*sin(self.theta1-self.theta2)
        num5 = (self.theta1_dot**2*r1*(m1+m2))
        num6 = g*(m1+m2)*cos(self.theta1)
        num7 = self.theta2_dot**2*r2*m2*cos(self.theta1-self.theta2)
        denom2 = r2*(2*m1+m2-m2*cos(2*self.theta1-2*self.theta2))
        theta2_dotdot = (num4*(num5+num6+num7))/denom2

        self.theta1_dot += theta1_dotdot * self.dt
        self.theta2_dot += theta2_dotdot * self.dt
        self.theta1 += self.theta1_dot * self.dt
        self.theta2 += self.theta2_dot * self.dt

        x1 = r1*sin(self.theta1)
        y1 = r1*cos(self.theta1)

        x2 = x1 + r2*sin(self.theta2)
        y2 = y1 + r2*cos(self.theta2)

        self.trace_coords.append((150 + x2, 60 + y2, 150 + x2, 60 + y2))
        self.canvas.create_line(self.trace_coords, fill='black', tag='trace')
        self.canvas.create_line(150,60,150+x1, 60+y1,width=3,fill='pink', tags='pendulum')
        self.canvas.create_line(150+x1, 60+y1,150+x2, 60+y2,width=3,fill='pink', tags='pendulum')
        self.canvas.create_oval(140+x1,50+y1,160+x1,70+y1,     fill='pink',outline='pink', tags='pendulum')
        self.canvas.create_oval(140+x2,50+y2,160+x2,70+y2, fill='pink',outline='pink', tags='pendulum')
        self.t += .1

        self.after(1, self.do_after)

if __name__ == '__main__':
    app = App()
    app.mainloop()