为什么我的 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 个矩形的速度发生。
编辑:
对于以后遇到此问题的任何人:
请检查你是否有一段代码创建对象实例,并在询问类似问题之前先删除它。
** 问题开头:**
我想用 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 个矩形的速度发生。