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

Viewed 94

I'm trying to learn tkinter, and I've found an animation of a ball (in this website).

I tried to modificate it to a ball that could be placed with the mouse. Until now I've done that first part, but I don't know how to add the movement. I suposse that the canvas need to be run before the code of the animation to obtain the ball coordinates.

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)
1 Answers

That is a really bad example. For most GUIs you should never use an infinite loop with sleep() in a GUI thread since it blocks GUI mainloop (pygame is a notable exception to this). Most GUIs, including tkinter, are "event-driven", and you need to use events to do things. The event I think you want is the mouse movement. The event will contain the mouse x, y position, so all that's left is to transfer that to the ball.

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()
Related