简体   繁体   English

为什么我的 Tkinter Game of Life 实现逐渐变慢?

[英]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.检查您是否有创建 object 实例的代码块,并在询问类似内容之前先将其删除。

** Start of the question:** ** 问题开始:**

I wanted to implement Conway's Game of Life with Tkinter.我想用 Tkinter 来实现康威的生命游戏。 My code does seem to work, but everytime I start the app, it slows down until the canvas only updates like every second.我的代码似乎确实有效,但每次我启动应用程序时,它都会变慢,直到 canvas 每秒更新一次。

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

The main function that is responsible for updating the canvas is the "startConway()" - function.负责更新 canvas 的主要 function 是“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.如您所见,我尝试测量时间 (t0,t1,t2) 以查看是否有一个 for 循环导致问题,但即使 canvas 变慢,时间几乎保持不变并且始终低于 20 毫秒。

Is there something I do not know about Tkinter that hinders my progress?关于 Tkinter 有什么我不知道的地方阻碍了我的进步吗? 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.在 canvas 上,单击一些像素,然后最后开始 Conway 游戏,单击黑色像素。

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.每次调用getAliveNeighbors时,都会创建一个新的Cell实例。 That creates a new rectangle on the canvas.这会在 canvas 上创建一个新矩形。 You call this function 100 times every 50 milliseconds (10 rows times 10 columns).您每 50 毫秒调用此 function 100 次(10 行乘以 10 列)。 So, every second you are creating 2,000 new rectangles.因此,您每秒钟都在创建 2,000 个新矩形。

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.一旦您在 canvas 上获得数万个对象,canvas 就会出现已知的性能问题,这将以每秒 2,000 个矩形的速度很快发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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