from tkinter import * from random import random import time # Good programming practice is to make these *variables* rather than "magic numbers" thrown into the code. canvas_height = 600 canvas_width = 800 world_top = 20 # 20 pixels from the top of the canvas world_ground = canvas_height - 20 # 20 pixels from bottom of the canvas world_left = 20 # 20 pixels from the left of the canvas world_right = canvas_width #This class represents the bouncing ball class #It is currently in here because it needs to access world's globals, but that was causing import problems class BouncingBall: #I considered randomly generating these values in this class, but decided that it was better from #a design point of view that the world decides which balls should be added to it (and this also more #easily allows extensions that allow the user to specify the ball parameters) def __init__(self, x, y): self.x = x self.y = y self.vx = random()+ .1 self.vy = random() self.radius = (random() *10) + 4 self.color = "black" #Get the x position of the ball def getX(self): return self.x #I think this is the main challenge in terms of program logic. #My way of thinking about the problem suggests that the checking whether it is over the x axis should be done in the world #Whereas the check for if it is under the ground should be done in this class def move(self): #Set the new x and y self.x += self.vx self.y += self.vy #Is y now below the ground? if self.y + self.radius >= world_ground: self.y = world_ground - self.radius#Sit it on ground level self.vy = self.vy * -1 #Reverse the y velocity #Is y above the top of the world? if self.y - self.radius <= world_top: self.y = world_top + self.radius #Make it so it is touching the top self.vy = self.vy * -1 #Reverse the y velocity #Draw the ball in the given canvas def draw(self): #The math here is potentially problematic. However, COMP102 students at Vic do these calculations in the second lab (flag drawing) canvas.create_oval(self.x - self.radius, self.y - self.radius, self.x + self.radius, self.y + self.radius, fill=self.color) #Code that controls the world that the bouncing balls are in #The world (but maybe not GUI) could be a class as well, although at this stage it isn't. bouncing_balls = [] def initialise_gui(): global canvas, number_of_balls_field window = Tk() Label(window, text="How many balls?").pack() number_of_balls_field = Entry(window) number_of_balls_field.pack() Button(window, text="Reset", command=start_animation).pack() canvas = Canvas(window, height=canvas_height, width=canvas_width, bg="white") canvas.pack() draw_world_outline() #I think this is good to seperate out, especially if they draw pretty brick patterns around the edge #And infact, with this design it is essential, as it will have to be redrawn every time... window.mainloop() 00 #Makes new random bounching balls objects and puts them into the list def list_of_balls(number_of_balls): balls = [] #Reset the bouncing balls list for i in range(number_of_balls): height = world_top+(world_ground-world_top)*random() ball = BouncingBall(world_left, height) balls.append(ball) return balls def start_animation(): #Read how many balls are needed count = int(number_of_balls_field.get()) balls = list_of_balls(count) while balls: #While there are still balls in the list (balls are removed when they go over the edge of the screen) #time.sleep-(1) remaining = [] for ball in balls: ball.move() if ball.getX() < world_right: remaining.append(ball) balls = remaining redraw_world(balls) def redraw_world(balls): canvas.delete(ALL) draw_world_outline() for ball in balls: ball.draw() canvas.update() def draw_world_outline(): canvas.create_line(world_right, world_top, world_left, world_top, world_left, world_ground, world_right, world_ground) #Line along the bottom #This is the "main" method. I think this is all we need to do, as the rest of the program is event driven input initialise_gui()