简体   繁体   中英

Why does my Tkinter Game of Life Implementation slow down progressively?

EDIT:

For anyone in the future having this problem:

PLEASE check whether or not you have a block of code that creates object instances and remove that first before asking something similar.

** Start of the question:**

I wanted to implement Conway's Game of Life with Tkinter. My code does seem to work, but everytime I start the app, it slows down until the canvas only updates like every second.

I have already looked into similar questions, but the problem here is that in every single one of them, the people continuously created rectangles which obviously causes frame rate problems, but I changed that in my implementation (I initialize the canvas with ROWS*COLS rectangles, then only change their color).

The main function that is responsible for updating the canvas is the "startConway()" - function. As you can see, i tried measuring the time (t0,t1,t2) to see whether or not one for loop is causing problems, but even though the canvas slows down, the times stay nearly constant and always under 20 ms.

Is there something I do not know about Tkinter that hinders my progress? Or is my approach just far to slow? Where can improvements be made?

If you want to try it out, just copy the code, then run it. On the canvas, click some pixels then finally to start the Conway game, click on a black pixel.

Thanks in advance.

Here is the code:

import tkinter as tk
import numpy as np
import time
import random
import copy
import math

# Set number of rows and columns
ROWS = 10
COLS = 10
ANIMATION_DELAY = 50
NEIGHBOUR_SEARCH_DELAY = 0
BORDER_PROB = 0.2
ITERATIONS_CONWAY = 1000

# Create a grid of None to store the references to the tiles
cells = [[None for _ in range(ROWS)] for _ in range(COLS)]

class Cell:

    def __init__(self, col, row):
        self.col = col
        self.row = row
        self.rect = None
        self.text = None
        createRectangle(self,"","","")
        # extras for A_Star:
        self.cost_g = 0
        self.cost = 0
        self.parent = None
        self.dist_to_neighbours = []
    
    def equals(self,cell):
        return (self.col == cell.col and self.row == cell.row)
    
    def changeText(self,txt,color):
        c.itemconfig(self.text,text=txt,fill=color)
        
    def changeRect(self,color):
        c.itemconfig(self.rect, fill=color)
    
    def toString(self):
        return str(self.col) + "|" + str(self.row)
    
    # extras for A_Star:
    def getDist(self,cell):
        return 1
    
def callback2(event):
    # Calculate column and row number
    col = int(event.x/col_width)
    row = int(event.y/row_height)
    clicked_cell = cells[col][row]
    # If the tile is not filled, create a rectangle
    if not c.itemcget(clicked_cell.rect,"fill") == "black":
        clicked_cell.changeRect("black")
    else:
            startConway(ITERATIONS_CONWAY)

def startConway(iteration):
    
    if iteration == 0:
        return
    
    alive = np.zeros([COLS,ROWS])
    
    t0 = time.time()
    
    for row in range(ROWS):
        for col in range(COLS):
            current_cell = cells[col][row]
            neighbours = getAliveNeighbours(current_cell)
            isAlive = c.itemcget(current_cell.rect,"fill") == "black"
            
            willSurvive = isAlive and (neighbours == 2 or neighbours == 3)
            willResurrect = not isAlive and neighbours == 3
            
            if willSurvive or willResurrect:
                alive[col][row] = 1
            else:
                alive[col][row] = 0
            
    t1 = time.time()
    
    for row in range(ROWS):
        for col in range(COLS):
            current_cell = cells[col][row]
            isAlive = alive[col][row]
            
            if isAlive:
                current_cell.changeRect("black")
            else:
                current_cell.changeRect("white")
                
    t2 = time.time()
    
    root.after(ANIMATION_DELAY,startConway,iteration-1)
       
    total = t1-t0
    
    print("neighbour Loop: ", t1-t0)
    print("draw Loop: ", t2-t1)
    print("----------------------")
    
def getAliveNeighbours(coord):
    count = 0
    
    upper_left = Cell(coord.col - 1,coord.row - 1) 
    neighbours = []
    for row in range(3):
        for col in range(3):
            new_col = (upper_left.col + col) % COLS
            new_row = (upper_left.row + row) % ROWS
            
            isStartingCell = (row == 1 and col == 1)
            isAlive = c.itemcget(cells[new_col][new_row].rect,"fill") == "black"
            
            if not isStartingCell and isAlive:
                count = count + 1
                
    return count

def createRectangle(cell,rect_color,txt,text_color):
        cell.rect = c.create_rectangle(cell.col*col_width, cell.row*row_height, (cell.col+1)*col_width, (cell.row+1)*row_height, fill=rect_color)
        cell.text = c.create_text(((cell.col*col_width + (cell.col+1)*col_width)/2, (cell.row*row_height + (cell.row+1)*row_height)/2), text=txt, fill=text_color)

def initializeCanvas(hasBorder):
    for row in range(ROWS):
        for col in range(COLS):
            cells[col][row] = Cell(col,row)
            if hasBorder and (row == 0 or col == 0 or col == COLS-1 or row == ROWS-1):
                cells[col][row].changeRect("black")
                
root = tk.Tk()

c = tk.Canvas(root, width=500, height=500, borderwidth=5, background='white')
c.pack()
c.bind("<Button-1>", callback2)
c.update()

col_width = c.winfo_width()/COLS
row_height = c.winfo_height()/ROWS

initializeCanvas(False)
            
root.mainloop()

Every time you call getAliveNeighbors you create a new Cell instance. That creates a new rectangle on the canvas. You call this function 100 times every 50 milliseconds (10 rows times 10 columns). So, every second you are creating 2,000 new rectangles.

The canvas has known performance issues once you get a few tens of thousands of objects on the canvas, which will happen pretty quickly at 2,000 rectangles per second.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM