Python 如何在函数调用中间更新TcTimter画布?
我正在用Tkinter编写Conway的生活游戏,我想有一个“Go”按钮,可以让动画开始,并继续自动步进直到结束。我使用画布来绘制环境,但由于“Go”按钮需要在更新画布之前完成函数调用,因此窗口将一直挂起,直到我终止该进程。我曾尝试在我想要更新画布的地方使用Python 如何在函数调用中间更新TcTimter画布?,python,tkinter,Python,Tkinter,我正在用Tkinter编写Conway的生活游戏,我想有一个“Go”按钮,可以让动画开始,并继续自动步进直到结束。我使用画布来绘制环境,但由于“Go”按钮需要在更新画布之前完成函数调用,因此窗口将一直挂起,直到我终止该进程。我曾尝试在我想要更新画布的地方使用canvas.update\u idletasks()和canvas.update()(之后是几秒钟的睡眠),但这似乎没有起到作用。有什么想法吗?下面是我的GameOfLife类,Environment类只是管理单元的“板” from Tki
canvas.update\u idletasks()
和canvas.update()
(之后是几秒钟的睡眠),但这似乎没有起到作用。有什么想法吗?下面是我的GameOfLife类,Environment类只是管理单元的“板”
from Tkinter import *
from random import *
from time import time
from Environment import *
class GameOfLife(object):
def __init__(self, master, envDim):
self.unitSize = 10
self.dimension = envDim * self.unitSize
self.environment = Environment(envDim)
self.environment.seedBoard()
self.started = False
frame = Frame(master)
frame.pack()
Button(frame, text = "Go", command = self.go_call).pack(side = LEFT)
Button(frame, text = "Clear", command = self.reset_call).pack(side = LEFT)
Button(frame, text = "Close", command = frame.quit).pack(side = RIGHT)
canvas = self.drawCanvas(master, self.dimension)
def drawCanvas(self, master, dimension):
self.canvas = Canvas(master, width = self.dimension, height = self.dimension)
self.canvas.pack()
return self.canvas
def go_call(self):
print "<< Go Call >>"
if self.environment.started == False:
self.environment.seedBoard()
self.drawState(self.environment)
self.environment.nextBoard()
self.started = True
while True:
self.environment.nextBoard()
self.canvas.delete(ALL)
self.drawState(self.environment)
self.canvas.update_idletasks()
sleep(4)
def reset_call(self):
print "<< Reset Call >>"
self.canvas.delete(ALL)
self.environment = Environment(self.environment.dim)
def drawState(self, environment):
size = self.unitSize
for x in range(environment.dim):
for y in range(environment.dim):
if environment.matrix[x][y].alive == True:
xs = x * size
ys = y * size
self.canvas.create_rectangle(xs, ys, xs+size, ys+size, fill = 'black')
envDim = 70
root = Tk()
gol = GameOfLife(root, envDim)
root.mainloop()
从Tkinter导入*
从随机导入*
从时间导入时间
从环境导入*
生命类别(对象):
定义初始化(自、主、环境):
self.unitSize=10
self.dimension=envDim*self.unitSize
self.environment=环境(envDim)
self.environment.seedBoard()
self.start=False
帧=帧(主帧)
frame.pack()
按钮(frame,text=“Go”,command=self.Go\u call).pack(side=LEFT)
按钮(frame,text=“Clear”,command=self.reset\u call).pack(side=LEFT)
按钮(frame,text=“Close”,command=frame.quit).pack(side=RIGHT)
canvas=self.drawCanvas(master,self.dimension)
def drawCanvas(自身、主控、尺寸):
self.canvas=canvas(主控,宽度=self.dimension,高度=self.dimension)
self.canvas.pack()
返回自我画布
def go_呼叫(自我):
打印“>”
如果self.environment.started==False:
self.environment.seedBoard()
self.drawState(self.environment)
self.environment.nextBoard()
self.start=True
尽管如此:
self.environment.nextBoard()
self.canvas.delete(全部)
self.drawState(self.environment)
self.canvas.update_idletasks()
睡眠(4)
def重置_呼叫(自):
打印“>”
self.canvas.delete(全部)
self.environment=environment(self.environment.dim)
def drawState(自身、环境):
大小=自身单位大小
对于范围内的x(environment.dim):
对于范围内的y(environment.dim):
如果environment.matrix[x][y].alive==True:
xs=x*大小
ys=y*尺寸
创建_矩形(xs,ys,xs+size,ys+size,fill='black')
envDim=70
root=Tk()
gol=生命的配子(根,环境)
root.mainloop()
您永远不应该在GUI程序中放入无限循环,而且绝对不应该调用sleep。您已经有一个无限循环在运行——事件循环——所以请充分利用它
制作这样的动画的方法是编写一个函数来绘制动画的一帧(或者游戏的回合,选择你的比喻)。然后,让该函数在之后通过调用自身
大致来说,您的代码应该如下所示:
def draw(self):
# draw the board according to the current state
...
# arrange for the next frame to draw in 4 seconds
self.after(4000, self.draw)
def __init__(self, ...):
...
self.go = tk.Button(self, text="Go", command=self.draw)
...
如果要添加停止按钮,只需设置一个标志。然后,在draw
中,只需在调用self.after
次要建议之前检查标志。似乎您正在删除所有画布对象,并在每次更新状态时创建新的画布对象。在画布上保持相同的矩形,并在每次迭代时更改其填充颜色可能会更有效。为什么睡眠4秒钟,不是那么多吗?与问题无关,但像示例代码中那样进行导入确实是个坏主意——除非您确切知道每个模块导出的内容,否则很容易出现名称冲突。根据经验,您永远不应该执行import*
。