我有以下代码正常工作,直到我在程序结束时添加了while循环,这基本上是为了保持循环永远运行(更新屏幕),直到窗口关闭。
while 1:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
将上述代码添加到现有代码后,程序会运行,但退出时......会出现错误:
_tkinter.TclError: can't invoke "update" command: application has been destroyed error
我在SO上看到了与此错误类似的问题,但没有针对此特定问题,并且没有一个答案可以帮助我的具体案例。
整个代码如下:
问题/问题:导致此错误的原因是什么?如何解决?
from tkinter import *
import random
import time
tk=Tk()
tk.title("My 21st Century Pong Game")
tk.resizable(0,0)
tk.wm_attributes("-topmost",1)
canvas=Canvas(tk,bg="white",width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
tk.update()
class Ball: #create a ball class
def __init__(self,canvas,color): #initiliased with the variables/attributes self, canvas, and color
self.canvas=canvas #set the intiial values for the starting attributes
self.id=canvas.create_oval(30,30,50,50,fill=color) #starting default values for the ball
""" Note: x and y coordinates for top left corner and x and y coordinates for the bottom right corner, and finally the fill colour for the oval
"""
self.canvas.move(self.id,0,0) #this moves the oval to the specified location
def draw(self): #we have created the draw method but it doesn't do anything yet.
pass
ball1=Ball(canvas,'green') #here we are creating an object (green ball) of the class Ball
while 1:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
解释更新:
还值得解释一下:
主循环是程序的中心部分,IDLE已经有一个主循环 - 但是如果你运行这个程序OUTSIDE的IDLE,画布会出现,然后在瞬间消失。为了阻止窗口关闭,我们需要一个动画循环 - 因此while 1:.....否则,正如用户在下面评论的那样,我们不需要while 1:因为IDLE已经有了这个地方(它在IDLE中工作正常而不使用1:..等等)
答案 0 :(得分:1)
您实际上是在尝试实施自己的mainloop
而不是使用它。
要了解mainloop
,您可以阅读here。您可以将其视为您添加的新代码的语法抽象;你的while
循环。你几乎试图通过自己的循环来重新创建轮子,然后更新屏幕",当一个已经存在时!
退出程序时,您可以使用sys.exit()
来避免错误。
修改强>
替换以下代码:
while 1:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
有了这个:
tk.mainloop()
您的错误是因为,正如它所说,如果窗口被销毁,您无法更新窗口。 mainloop
应处理此问题。
答案 1 :(得分:1)
退出应用时,下次调用update_idletasks
时,窗口对象将被销毁。然后尝试在不存在的窗口上调用update
。
您需要删除以while
开头的所有四行,并将其替换为正确处理窗口销毁的单行tk.mainloop()
。
此外,如果您想要保留原始代码,则没有理由同时调用update_idletasks
和update
。前者是后者的一个子集。
答案 2 :(得分:1)
我同意其他人你应该在这里使用mainloop()
但是如果你想保持原始代码的方式我会这样做是跟踪一个布尔值并改为while x == True
。这样我们就可以将x的值更新为等于False
,这样可以防止错误发生。
我们可以使用protocol()
方法在应用关闭时更新我们的布尔值。
如果我们将此添加到您的代码中:
x = True
def update_x():
global x
x = False
tk.protocol("WM_DELETE_WINDOW", update_x)
并将您的while语句更改为:
while x == True:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
所以你的完整代码可能如下所示:
from tkinter import *
import random
import time
tk=Tk()
tk.title("My 21st Century Pong Game")
tk.resizable(0,0)
tk.wm_attributes("-topmost",1)
x = True
def update_x():
global x
x = False
tk.protocol("WM_DELETE_WINDOW", update_x)
canvas=Canvas(tk,bg="white",width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
tk.update()
class Ball:
def __init__(self,canvas,color):
self.canvas=canvas
self.id=canvas.create_oval(30,30,50,50,fill=color)
""" Note: x and y coordinates for top left corner and x and y coordinates for the bottom right corner, and finally the fill colour for the oval
"""
self.canvas.move(self.id,0,0)
def draw(self):
pass
ball1=Ball(canvas,'green')
while x == True:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
这将解决您的问题。
重申其他人所说的,你真正需要的只是mainloop()
,而不是while i:
声明。
mainloop()
方法用于重置Tk()
实例的循环。一旦代码到达tk.mainloop()
的行,那么它将成为您的下一个循环代码。
编写代码的正确方法是使用mainloop()
,因为它会对tkinter实例进行所有更新。
使用mainloop()
:
from tkinter import *
tk=Tk()
tk.title("My 21st Century Pong Game")
tk.resizable(0,0)
tk.wm_attributes("-topmost",1)
canvas=Canvas(tk,bg="white",width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
tk.update()
class Ball:
def __init__(self,canvas,color):
self.canvas=canvas
self.id=canvas.create_oval(30,30,50,50,fill=color)
""" Note: x and y coordinates for top left corner and x and y coordinates for the bottom right corner, and finally the fill colour for the oval
"""
self.canvas.move(self.id,0,0)
def draw(self):
pass
ball1=Ball(canvas,'green')
tk.mainloop()