为什么我的 Tkinter 生命游戏实施会逐渐变慢?

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

编辑:

对于以后遇到此问题的任何人:

检查你是否有一段代码创建对象实例,并在询问类似问题之前先删除它。

** 问题开头:**

我想用 Tkinter 实现 Conway 的生命游戏。我的代码似乎确实有效,但每次我启动该应用程序时,它都会变慢,直到 canvas 每秒只更新一次。

我已经研究过类似的问题,但这里的问题是,在每一个问题中,人们不断地创建矩形,这显然会导致帧速率问题,但我在我的实现中改变了它(我初始化 canvas ROWS*COLS 矩形,然后只改变它们的颜色)。

负责更新 canvas 的主要函数是“startConway()”函数。如您所见,我尝试测量时间 (t0,t1,t2) 以查看一个 for 循环是否导致问题,但即使 canvas 变慢,时间几乎保持不变并且始终低于 20女士

关于 Tkinter 有什么我不了解的东西阻碍了我的进步吗?还是我的方法太慢了?哪里可以改进?

如果您想尝试一下,只需复制代码,然后 运行 即可。在 canvas 上,点击一些像素,然后最后开始 Conway 游戏,点击一个黑色像素。

提前致谢。

代码如下:

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

每次调用 getAliveNeighbors 都会创建一个新的 Cell 实例。这会在 canvas 上创建一个新矩形。您每 50 毫秒调用此函数 100 次(10 行乘以 10 列)。因此,您每秒都在创建 2,000 个新矩形。

一旦您在 canvas 上获得数万个对象,canvas 就会出现已知的性能问题,这种情况会以每秒 2,000 个矩形的速度发生。