Reputation: 1
I am learning Python canvas Tkinter, I am trying to make a ball animation continuously moving from top left corner to bottom right.
The problem is that there is a ball when it leaves the top corner and another in the bottom right.
the ball in the middle is moving from top left to bottom right
Here is the code:
from graphics import Canvas
CANVAS_WIDTH = 400
CANVAS_HEIGHT = 400
BALL_SIZE = 50
DELAY =0.001
def main():
canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT)
ball = canvas.create_oval(0,0 ,BALL_SIZE,BALL_SIZE, 'blue')
change_x = 1
change_y = 1
while (True):
left_x = canvas.get_left_x(ball)
top_y = canvas.get_top_y(ball)
if left_x < 0 or left_x + BALL_SIZE >= CANVAS_WIDTH:
ball = canvas.create_oval(change_x,change_y ,BALL_SIZE,BALL_SIZE, 'blue')
if top_y < 0 or top_y + BALL_SIZE >= CANVAS_HEIGHT:
ball = canvas.create_oval(change_x,change_y ,BALL_SIZE,BALL_SIZE, 'blue')
canvas.move(ball,change_x, change_y)
time.sleep(DELAY)
if __name__ == '__main__':
main()
I want the ball to move from top left to bottom right without being in the top left or bottom right.
Upvotes: 0
Views: 111
Reputation: 143206
Don't create new ball but only change values in change_x
, change_y
to change direction.
But you have to use if
with different values
if left_x < 0 or top_y < 0 :
change_x = 1
change_y = 1
if left_x + BALL_SIZE >= CANVAS_WIDTH or top_y + BALL_SIZE >= CANVAS_HEIGHT:
change_x = -1
change_y = -1
And don't use while
-loop and time.sleep()
but put code in function e.g. move()
and add canvas.after(DELAY, move)
in this function this function to inform tkinter
that it has to execute it again after DELAY milliseconds.
(after
needs milliseconds instead of seconds - 0.001s = 1ms
)
(usually we put after()
at the end but it can be in any place because it doesn't block code and it doesn't exit function - it only inform tkinter
when it has to run this function again)
def move():
# ... code ...
# run `move()` after `DELAY` milliseconds
canvas.after(DELAY, move)
And it needs to run mainloop()
to keep running all code. mainloop
checks if there are new mouse/key event, redraw elements in windows, and it executes move
after DELAY
milliseconds.
Full working code:
from graphics import Canvas
CANVAS_WIDTH = 400
CANVAS_HEIGHT = 400
BALL_SIZE = 50
DELAY = 1 # milliseconds instead of seconds
def move():
global change_x
global change_y
left_x = canvas.get_left_x(ball)
top_y = canvas.get_top_y(ball)
if left_x < 0 or top_y < 0 :
change_x = 1
change_y = 1
if left_x + BALL_SIZE >= CANVAS_WIDTH or top_y + BALL_SIZE >= CANVAS_HEIGHT:
change_x = -1
change_y = -1
canvas.move(ball,change_x, change_y)
# run `move()` after `DELAY` milliseconds
canvas.after(DELAY, move) # it needs milliseconds instead of seconds
def main():
global change_x
global change_y
global canvas
global ball
change_x = 1
change_y = 1
canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT)
ball = canvas.create_oval(0, 0, BALL_SIZE, BALL_SIZE, 'blue')
move()
canvas.mainloop() # keep running application (check mouse/key events and redraw elements in window
if __name__ == '__main__':
main()
Version which uses wider canvas and ball changes direction in more interesting way.
And ball changes color when it touches border.
And ball stops for 500ms when it touches border.
from graphics import Canvas
CANVAS_WIDTH = 1200
CANVAS_HEIGHT = 400
BALL_SIZE = 50
DELAY = 1 # milliseconds instead of seconds
LONGER_DELAY = 500
def move():
global change_x
global change_y
delay = DELAY
left_x = canvas.get_left_x(ball)
top_y = canvas.get_top_y(ball)
# left - right
if left_x < 0:
change_x = -change_x
canvas.set_fill_color(ball, 'blue')
delay = LONGER_DELAY
elif left_x + BALL_SIZE >= CANVAS_WIDTH:
change_x = -change_x
canvas.set_fill_color(ball, 'red')
delay = LONGER_DELAY
# top - bottom
if top_y < 0:
change_y = -change_y
canvas.set_outline_color(ball, 'green')
delay = LONGER_DELAY
elif top_y + BALL_SIZE >= CANVAS_HEIGHT:
change_y = -change_y
canvas.set_outline_color(ball, 'yellow')
delay = LONGER_DELAY
canvas.move(ball, change_x, change_y)
# run `move()` after `DELAY` milliseconds
canvas.after(delay, move) # it needs milliseconds instead of seconds
def main():
global change_x
global change_y
global canvas
global ball
change_x = 1
change_y = 1
canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT)
ball = canvas.create_oval(0, 0, BALL_SIZE, BALL_SIZE, 'blue', 'black', 15)
move()
#canvas.after(DELAY, move)
canvas.mainloop()
if __name__ == '__main__':
main()
And last idea - many balls with random speeds
from graphics import Canvas
import random
CANVAS_WIDTH = 1200
CANVAS_HEIGHT = 400
BALL_SIZE = 50
DELAY = 1 # milliseconds instead of seconds
def move():
for item in all_items:
left_x = canvas.get_left_x(item['id'])
top_y = canvas.get_top_y(item['id'])
# left - right
if left_x < 0:
item['change_x'] = -item['change_x']
canvas.set_fill_color(item['id'], 'blue')
elif left_x + BALL_SIZE >= CANVAS_WIDTH:
item['change_x'] = -item['change_x']
canvas.set_fill_color(item['id'], 'red')
# top - bottom
if top_y < 0:
item['change_y'] = -item['change_y']
elif top_y + BALL_SIZE >= CANVAS_HEIGHT:
item['change_y'] = -item['change_y']
canvas.move(item['id'], item['change_x'], item['change_y'])
# run `move()` after `DELAY` milliseconds
canvas.after(DELAY, move) # it needs milliseconds instead of seconds
def main():
global canvas
global all_items
all_items = []
canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT)
for x in range(0, 1200, 200):
ball = canvas.create_oval(x, 0, x+BALL_SIZE, BALL_SIZE, 'blue')
item = {
'id': ball,
'change_x': random.randint(1, 3),
'change_y': random.randint(1, 3),
}
all_items.append(item)
move()
#canvas.after(DELAY, move)
canvas.mainloop()
if __name__ == '__main__':
main()
Upvotes: 0