我是编程新手,我正在尝试制作一个简单的动画以更好地学习。我刚刚学习了python(仍然是nooby)并开始学习tkinter。 我正在尝试制作Conway的《人生游戏》的动画,因为它具有非常简单的原理并且看上去很酷。 我设法设法使我的代码正常工作,但我真的不明白如何做。 问题是我无法理解其工作原理之后。
我不理解的代码部分是称为start的方法。 我真的不明白在startloop函数返回None之前如何打印“循环完成”(这应该与说动画还没有停止一样)
import tkinter as tk
width = 1400
height = 600
dist = 5
drawlines = False
celstate = set()
numcol = width//dist
numrow = height//dist
def getdeadcells(setcells):
global celstate
deadcells = set()
for cell in setcells:
i, j = cell
list = [(i-1, j-1), (i, j-1), (i+1, j-1),
(i-1, j), (i+1, j), (i-1, j+1), (i, j+1), (i+1, j+1)]
for cel in list:
if cel not in celstate:
deadcells.add(cel)
return deadcells
def getnewstate():
def neight(cell):
i, j = cell
count = 0
list = [(i-1, j-1), (i, j-1), (i+1, j-1),
(i-1, j), (i+1, j), (i-1, j+1), (i, j+1), (i+1, j+1)]
for cel in list:
if cel in celstate:
count +=1
return count
global celstate, numcol, numrow
alivecells = celstate.copy()
deadcells = getdeadcells(alivecells)
newstate = set()
for cell in alivecells:
neigh = neight(cell)
if neigh == 2 or neigh == 3:
newstate.add(cell)
for cell in deadcells:
neigh = neight(cell)
if neigh == 3:
newstate.add(cell)
if newstate == celstate:
return None
else:
celstate = newstate
if len(newstate) == 0:
return ""
else:
return newstate
def getcords(x, y):
col = x//dist
row = y//dist
return (col, row)
class GUI():
def __init__(self, master, width, height, dist):
master.geometry("{}x{}".format(width, height))
master.bind("<Key>", self.start)
self.master = master
self.width = width
self.height = height
self.dist = dist
self.canvas = tk.Canvas(master, width=width, height=height)
self.canvas.pack(expand=True)
self.drawlimits(dist)
def start(self, event):
if event.keycode == 32 or event.keycode == 13:
def startloop():
newstate = getnewstate()
if newstate == None:
return None
elif newstate == "":
self.canvas.delete("rect")
return None
else:
self.canvas.delete("rect")
self.fillrects(list(newstate))
self.master.after(100, startloop)
startloop()
print("loop finished")
def drawlimits(self, dist):
if self.width % dist == 0 and self.height % dist == 0:
self.canvas.bind("<B1-Motion>", self.drawcells)
self.canvas.bind("<ButtonRelease-1>", self.drawcells)
self.canvas.bind("<B3-Motion>", self.killcell)
self.canvas.bind("<ButtonRelease-3>", self.killcell)
if drawlines:
xsteps = self.width/dist
ysteps = self.height/dist
for num in range(int(xsteps-1)):
self.canvas.create_line((num+1)*dist, 0, (num+1)*dist, self.height)
for num in range(int(ysteps-1)):
self.canvas.create_line(0, (num+1)*dist, self.width, (num+1)*dist)
def drawcells(self, event):
cell = getcords(event.x, event.y)
if cell not in celstate:
self.fillrects([cell])
celstate.add(cell)
def killcell(self, event):
cell = getcords(event.x, event.y)
if cell in celstate:
celstate.remove(cell)
col, row = cell
tag = "{},{}".format(col, row)
obj.canvas.delete(tag)
def fillrects(self, cords):
for gcords in cords:
col, row = gcords
tag = "{},{}".format(col,row)
dist = self.dist
self.canvas.create_rectangle(col*dist, row*dist, (col+1)*dist, (row+1)*dist,
fill="black", tags=(tag, "rect"))
root = tk.Tk()
obj = GUI(root, width, height, dist)
root.mainloop()
代码工作如下: 我仅将存活的细胞保存在celstate集中。 然后,我发现可能死活的死细胞并遍历
中死活的细胞如果celstate与先前的相同,或者没有活动的单元格,则函数getnewstate返回None。
然后在start方法中,我调用函数getnewstate并绘制其内容,直到celstate返回None(使用startloop函数使用after方法调用其自身)。 我不明白,如果startloop还没有停止,为什么可以打印“循环完成”。 即使我不理解这部分内容,代码仍然可以按预期工作,这对我来说更加令人生厌。 任何人都可以帮助澄清发生了什么事吗?
我确定问题是因为我不太了解mainloop的工作原理
答案 0 :(得分:2)
tkinter after
方法有效地将消息发送到mainloop()以在n毫秒内运行回调函数。您的start
函数发送此消息,然后打印“循环完成”。在执行之前,它不等待after回调返回。 100毫秒后,它将调用startloop()并重新计算并显示新的网格。如果确实等待回调返回,它将在等待时冻结UI。 after函数使您可以在延迟后运行代码,但仍具有活动的ui。
我修改了您的启动函数,以打印“循环完成”,而不是在代码的退出部分不返回None。
def start(self, event):
if event.keycode == 32 or event.keycode == 13:
def startloop():
newstate = getnewstate()
if newstate == None:
print("loop finished")
elif newstate == "":
self.canvas.delete("rect")
print("loop finished")
else:
self.canvas.delete("rect")
self.fillrects(list(newstate))
self.master.after(100, startloop)
startloop()
您可能遇到的一个问题是,生活游戏可以达到稳定的条件,每两个周期就会恢复到相同的状态。有些形状的周期甚至更长。
HTH