after 方法在 tkinter 中如何工作?
How does the after method works in tkinter?
我是编程新手,我正在尝试制作一个简单的动画以更好地学习。刚刚学了python(还是nooby)开始学tkinter。
我正在尝试制作康威生命游戏的动画,因为它的原理非常简单而且看起来很酷。
我已经设法让我的代码正常工作,但我真的不明白怎么做。
问题是我无法理解它是如何工作的。
我不明白的代码部分是名为 start 的方法。
我真的不明白"loop finished"怎么可以在startloop函数returnsNone之前打印(这应该和说动画还没有停止一样)
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与之前的celstate相同或者没有活细胞:那么函数getnewstate returns None.
然后在 start 方法中,我调用函数 getnewstate 并绘制其内容,直到 celstate returns None(使用 startloop 函数通过 after 方法调用自身)。
我不明白如果 startloop 还没有停止,为什么可以打印 "loop finished"。
即使我不理解这部分,代码仍然按预期工作,这对我来说更加烦人。
谁能帮忙弄清楚这是怎么回事??
- dist 变量表示以像素为单位的像元大小
- 您可以使用鼠标左键绘制新单元格或使用右键删除现有单元格。 (很酷的部分是你可以在动画还在播放的时候这样做)
我确定问题来了,因为我不太了解主循环的工作原理
tkinter after
方法有效地在 n 毫秒内将消息发送到 mainloop() 到 运行 回调函数。您的 start
函数发送此消息然后打印 "loop finished"。在继续执行之前,它不会等待 return 的 after 回调。 100 毫秒后,它调用 startloop() 并重新计算并显示新网格。如果它确实等待 return 的回调,它会在等待时冻结 UI。 after 函数允许您在延迟后 运行 编码,但仍然有一个活动的 ui.
我已经修改了您的启动函数以在代码的退出部分打印 "loop finished" 而不是 returning 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()
您可能遇到的一个问题是,生命游戏可以达到每两个周期 return 相同状态的稳定条件。有些形状的循环周期甚至更长。
HTH
我是编程新手,我正在尝试制作一个简单的动画以更好地学习。刚刚学了python(还是nooby)开始学tkinter。 我正在尝试制作康威生命游戏的动画,因为它的原理非常简单而且看起来很酷。 我已经设法让我的代码正常工作,但我真的不明白怎么做。 问题是我无法理解它是如何工作的。
我不明白的代码部分是名为 start 的方法。 我真的不明白"loop finished"怎么可以在startloop函数returnsNone之前打印(这应该和说动画还没有停止一样)
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与之前的celstate相同或者没有活细胞:那么函数getnewstate returns None.
然后在 start 方法中,我调用函数 getnewstate 并绘制其内容,直到 celstate returns None(使用 startloop 函数通过 after 方法调用自身)。 我不明白如果 startloop 还没有停止,为什么可以打印 "loop finished"。 即使我不理解这部分,代码仍然按预期工作,这对我来说更加烦人。 谁能帮忙弄清楚这是怎么回事??
- dist 变量表示以像素为单位的像元大小
- 您可以使用鼠标左键绘制新单元格或使用右键删除现有单元格。 (很酷的部分是你可以在动画还在播放的时候这样做)
我确定问题来了,因为我不太了解主循环的工作原理
tkinter after
方法有效地在 n 毫秒内将消息发送到 mainloop() 到 运行 回调函数。您的 start
函数发送此消息然后打印 "loop finished"。在继续执行之前,它不会等待 return 的 after 回调。 100 毫秒后,它调用 startloop() 并重新计算并显示新网格。如果它确实等待 return 的回调,它会在等待时冻结 UI。 after 函数允许您在延迟后 运行 编码,但仍然有一个活动的 ui.
我已经修改了您的启动函数以在代码的退出部分打印 "loop finished" 而不是 returning 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()
您可能遇到的一个问题是,生命游戏可以达到每两个周期 return 相同状态的稳定条件。有些形状的循环周期甚至更长。
HTH