2017-09-22 204 views
0

我有以下代码工作正常,直到我在程序结束时添加了while循环,基本上试图保持循环运行永远(更新屏幕),直到窗口关闭。_tkinter.TclError:无法调用“更新”命令:应用程序已被破坏错误

while 1: 
     tk.update_idletasks() 
     tk.update() 
     time.sleep(0.01) 

在加入上面的代码,以现有的代码,程序运行,但在出口,与此错误出现:

_tkinter.TclError: can't invoke "update" command: application has been destroyed error 

我已经看到了类似的问题与此对于这个特定的问题没有任何答案可以帮助我解决这个问题。

整个代码如下:

问题/问题:是什么原因造成这个错误,我该如何解决?

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已经有一个主循环 - 但是如果你运行这个程序在IDLE之外,画布将出现,然后在瞬间消失后消失。要阻止窗口关闭,我们需要一个动画循环 - 因此while 1:.....否则,作为用户在下面评论,我们不需要while 1:因为IDLE已经有了这个地方(它可以在IDLE中正常工作,而无需使用while 1:..等等)

+1

只是固定的代码缩进 - 对不起 – MissComputing

+0

边注:避免使用'睡眠()'和'的wait()'用Tkinter的。这些方法对tkinter来说效果不佳。改用'after()'。 –

回答

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() 
+0

如果正确的解决方案是用一行代码替换所有的代码,则您的解决方案需要四行低效代码并将其转换为九。 –

+0

@BryanOakley正如我在我的回答中所说的,最好的选择是使用'mainloop()'我明确表示。我只是为OP提供了一种功能性的方式来使用他们的原始代码,以便他们可以看到他们做错了什么。 –

1

您正在有效地尝试实现您自己的mainloop而不是使用它。

要了解mainloop,您可以阅读关于它here。您可以将其视为您添加的新代码的语法抽象;您的while循环。当你已经存在的时候,你几乎试图通过创建自己的循环来“更新屏幕”来重新创建轮子!

退出程序时,可以使用sys.exit()来避免错误。

编辑:

替换下面的代码:

while 1: 
    tk.update_idletasks() 
    tk.update() 
    time.sleep(0.01) 

有了这个:

tk.mainloop() 

你的错误是因为,它说,你不能更新,如果它的窗口已被销毁。 mainloop应该处理这个问题。

+1

SneakyTurtle - 这绝对没有意义。您可以发布我需要使用的代码 - 理想情况下,我想上传代码! :-)你是什么意思用tk.mainloop()替换“while”?请澄清 – MissComputing

+0

同意 - 这不会帮助 - 除非您可以澄清或张贴代码/答案。当然可以退出并避免使用sys.exit(),但是在什么情况下以及如何使用? – 2017-09-22 18:00:10

+0

@MissComputing @pythoncarrot,我编辑了我的主帖,试图解释'mainloop'是什么。 – SneakyTurtle

1

当您退出应用程序时,下次打电话给update_idletasks窗口对象被销毁。然后尝试在不存在的窗口上拨打update

您需要删除以while开头的所有四行,并用适当处理窗口破坏的单行tk.mainloop()替换它们。另外,如果您想保留原始代码,则没有理由同时拨打update_idletasksupdate。前者是后者的一个子集。

相关问题