2

I wrote a Python program with Tkinter that makes a ball bounce around the screen. It works great, except for one problem: the outermost edges of the ball flicker as the ball moves.

I understand Tkinter automatically does double buffering, so I thought I shouldn't be having problems with tearing. I'm not sure where the error is coming from. Any ideas on how I can get rid of it?

Code is below. Here's the gist of it: class Ball extends Tk and gets created when the program is run. It sets up the game, encapsulating it in a GModel. GModel then runs the game with run() and just loops over and over, calling update().

import threading
from time import sleep, clock
from tkinter import *
from tkinter import ttk

SPEED = 150  # Pixels per second
WIDTH = 400
HEIGHT = 500
RAD = 25
PAUSE = 0  # Time added between frames.  Slows things down if necessary

class GModel:
    # Contains the game data.

    def __init__(self, can):
        # can is Canvas to draw on
        self.can = can
        self.circ = can.create_oval(0,0, 2*RAD, 2*RAD)

        # Position and velocity of the ball
        self.x, self.y = RAD, RAD
        self.dx, self.dy = SPEED, SPEED

        # Ball is moving?
        self.is_running = True

    def update(self, elapsed_time):
        # Updates the dynamic game variables.  elapsed_time is in seconds.
        change_x = self.dx * elapsed_time
        change_y = self.dy * elapsed_time

        self.can.move(self.circ, change_x, change_y)
        self.x += change_x
        self.y += change_y

        self.resolve_collisions()

    def resolve_collisions(self):
        # If the ball goes off the edge, put it back and reverse velocity
        if self.x - RAD < 0:
            self.x = RAD
            self.dx = SPEED
        elif self.x + RAD > WIDTH:
            self.x = WIDTH - RAD
            self.dx = -SPEED

        if self.y - RAD < 0:
            self.y = RAD
            self.dy = SPEED
        elif self.y + RAD > HEIGHT:
            self.y = HEIGHT - RAD
            self.dy = -SPEED

    def end_game(self):
        self.is_running = False

    def run(self):
        last_time = 0.0

        while self.is_running:
            # Get number of seconds since last iteration of this loop
            this_time = clock()
            elapsed_time = this_time - last_time
            last_time = this_time

            try:   # Use this here in case the window gets X'd while t still runs
                self.update(elapsed_time)
            except:
                self.is_running = False

            if (PAUSE > 0):
                sleep(PAUSE)  # Slow things down if necessary

class Ball(Tk):
    def __init__(self):
        Tk.__init__(self)

        self.can = Canvas(self, width=WIDTH, height=HEIGHT)
        self.can.grid(row=0, column=0, sticky=(N, W, E, S))
        self.gm = GModel(self.can)

    def mainloop(self):
        t = threading.Thread(target=self.gm.run, daemon=True)
        t.start()
        Tk.mainloop(self)

def main():
    Ball().mainloop()

if __name__ == "__main__":
    main()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
Axesilo
  • 113
  • 3
  • 8
  • 1
    Are you aware that you don't need threads (or sleep) to do basic animations in tkinter? See http://stackoverflow.com/a/25431690/7432 – Bryan Oakley Jul 28 '16 at 12:20
  • Ah, thank you - "don't put an infinite loop in a GUI" is a good design practice I wasn't aware of. – Axesilo Jul 28 '16 at 20:04

0 Answers0