(Tkinter) 如何 运行 放置对象后的动画?

(Tkinter) How run an animation after put the object?

我正在尝试学习 tkinter,我发现了一个球的动画 (in this website)。

我试着修改成一个可以用鼠标放置的球。到目前为止,我已经完成了第一部分,但我不知道如何添加动作。我认为 canvas 需要在动画代码之前 运行 才能获取球坐标。

import tkinter 
import time


animation_window_width=800
animation_window_height=600
animation_ball_radius = 30
animation_ball_min_movement = 5
animation_refresh_seconds = 0.01

def animate_ball(window, canvas, xinc, yinc, ball):

    while True:
        canvas.move(ball,xinc,yinc)
        window.update()
        time.sleep(animation_refresh_seconds)
        ball_pos = canvas.coords(ball)
        xl,yl,xr,yr = ball_pos
        if xl < abs(xinc) or xr > animation_window_width-abs(xinc):
            xinc = -xinc
        if yl < abs(yinc) or yr > animation_window_height-abs(yinc):
            yinc = -yinc


def position_ball(event): 
    ball = canvas.create_oval(event.x-animation_ball_radius,
               event.y-animation_ball_radius,
               event.x+animation_ball_radius,
               event.y+animation_ball_radius,
               fill="blue", outline="white", width=4)
    
window = tkinter.Tk()
window.title("Tkinter Animation Demo")
window.geometry(f'{animation_window_width}x{animation_window_height}')
canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)
ball=canvas.bind("<Button-1>", position_ball)
ball 

#animate_ball(window, canvas, animation_ball_min_movement, animation_ball_min_movement, ball)

这是一个非常糟糕的例子。对于大多数 GUI,您不应该在 GUI 线程中使用带有 sleep() 的无限循环,因为它会阻塞 GUI 主循环(pygame 是一个明显的例外)。大多数 GUI,包括 tkinter,都是“事件驱动的”,你需要使用事件来做事。我想你想要的事件是鼠标移动。该事件将包含鼠标的 x、y 位置,所以剩下的就是将其传递给球。

import tkinter

animation_window_width=800
animation_window_height=600
animation_ball_radius = 30
animation_ball_min_movement = 5
animation_refresh_seconds = 0.01

def place_ball(event):
    canvas.unbind("<Motion>") # stop responding to motion
    canvas.xinc = animation_ball_min_movement
    canvas.yinc = animation_ball_min_movement
    animate_ball()

def locate_ball(event):
    canvas.coords(ball,
        event.x-animation_ball_radius,
        event.y-animation_ball_radius,
        event.x+animation_ball_radius,
        event.y+animation_ball_radius)

# Create and animate ball in an infinite loop
def animate_ball(event=None):
    canvas.move(ball,canvas.xinc,canvas.yinc) # move the ball
    xl,yl,xr,yr = canvas.coords(ball) # get current coordinates

    if xl < abs(canvas.xinc) or xr > animation_window_width-abs(canvas.xinc):
      canvas.xinc = -canvas.xinc
    if yl < abs(canvas.yinc) or yr > animation_window_height-abs(canvas.yinc):
      canvas.yinc = -canvas.yinc

    canvas.coords(ball, xl,yl,xr,yr) # set new coordinates

    canvas.after(20, animate_ball) # set the loop event

window = tkinter.Tk()
window.title("Tkinter Animation Demo")
window.geometry(f'{animation_window_width}x{animation_window_height}')
canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)
ball = canvas.create_oval(0,0,0,0,fill="blue", outline="white", width=4)
canvas.bind('<Motion>', locate_ball)
canvas.bind('<Button-1>', place_ball)
window.mainloop()