2015-08-14 163 views
0

我遇到与tkinter界面窗口关闭按钮的一些问题。我的工具可以实时显示一些视频,我可以通过after函数实现无限循环。Tkinter退出命令与无限循环

当我通过点击十字关闭tkinter窗口时,程序冻结。但是,当我点击按钮时,会调用相同的函数,但它会正确关闭。

这里是我提出的最简单的代码来向你展示问题。有没有人有解释和解决办法?

(顺便说一句,我使用Python 2.7.8在OSX)

from Tkinter import * 
from PIL import Image, ImageTk 
import numpy as np 

class Test(): 
    def __init__(self, master): 
     self.parent = master 
     self.frame = Frame(self.parent) 
     self.frame.pack(fill=BOTH, expand=1) 
     self.mainPanel = Label(self.frame) 
     self.mainPanel.pack(fill=BOTH, expand=1) 
     self.closeButton = Button(self.frame, command=self.closeApp) 
     self.closeButton.pack(fill=BOTH, expand=1) 

    def closeApp(self): 
     print "OVER" 
     self.parent.destroy() 

def task(tool): 
    print 'ok' 
    im = Image.fromarray(np.zeros((500, 500, 3)), 'RGB') 
    tool.tkim = ImageTk.PhotoImage(im) 
    tool.mainPanel['image'] = tool.tkim 
    root.after(1, task, tool) 

def on_closing(): 
    print "OVER" 
    root.destroy() 

root = Tk() 
root.wm_protocol("WM_DELETE_WINDOW", on_closing) 
tool = Test(root) 
root.after(1, task, tool) 
root.mainloop() 

现在,如果你有一个更小的图像再试一次(比如100 * 100),它的工作原理。或者,如果您在后续功能中放置了延迟100,它也可以工作。但在我的应用程序中,我需要一个非常短暂的延迟时间,因为我正在显示视频,而且图像尺寸为900px * 500px。

谢谢!

编辑(08/19):我还没有找到解决方案。但我可以使用root.overrideredirect(1)删除关闭按钮,然后在Tk中重新创建它,并添加拖动窗口使用:Python/Tkinter: Mouse drag a window without borders, eg. overridedirect(1)

编辑(08/20):其实,我甚至不能拖动窗口。该工具也冻结!

+0

http://stackoverflow.com/a/17016127/4731042参见布莱恩奥克利:“我建议不要使用后为1ms,除非你真正需要做的是1000次。”在http://stackoverflow.com/questions/28047746/access-to-tkinters-text-widget-by-background-thread-cause-crashes –

+0

感谢您的链接。就我而言,它无限期地冻结,可能是因为无限循环。如果只是为了递归任务功能而冻结,那根本就不成问题。 Bryan Oakley可能是对的。问题是我真的需要延迟<25ms,因为我的视频是40fps。 – Hugo

回答

0

我发现了一个解决方案,我不确定它是否真的很干净,但至少它正在为我想要做的工作而工作。我不再使用后,但我循环和更新gui在每次迭代。

from Tkinter import * 
from PIL import Image, ImageTk 
import numpy as np 


class Test(): 
    def __init__(self, master): 
     self.parent = master 
     self.frame = Frame(self.parent) 
     self.frame.pack(fill=BOTH, expand=1) 
     self.mainPanel = Label(self.frame) 
     self.mainPanel.pack(fill=BOTH, expand=1) 
     self.parent.wm_protocol("WM_DELETE_WINDOW", self.on_closing) 
     self.close = 0 

    def on_closing(self): 
     print "Over" 
     self.close = 1 

    def task(self): 
     print "ok" 
     im = Image.fromarray(np.zeros((500, 500, 3)), 'RGB') 
     self.tkim = ImageTk.PhotoImage(im) 
     self.mainPanel['image'] = self.tkim 

root = Tk() 
tool = Test(root) 

while(tool.close != 1): 
    tool.task() 
    root.update() 
root.destroy() 
1

你可能只需要杀死你的动画循环。 after返回可用于取消挂起作业的作业ID。

def task(): 
    global job_id 
    ... 
    job_id = root.after(1, task, tool) 

def on_closing(): 
    global job_id 
    ... 
    root.after_cancel(job_id) 

你的代码可能是有点清洁,如果这些功能是该对象的方法,所以你不必使用全局变量。此外,你应该有一个退出功能,而不是两个。或者,让其他人调用另一个,以确保两者都经历完全相同的代码路径。

最后,除非您确实需要,否则不应该每秒调用一次1000次的函数。经常调用它会让你的UI呆滞。

+0

这太棒了,谢谢!并感谢提示。实际上我会在'25 - app_time'后面调用(其中'app_time'是处理图像的时间),因为我需要视频的帧率为40 fps。 – Hugo

+0

对不起,它不工作,对于一些测试,我把20ms,它的工作,但设置一些较小的价值,它不再工作。实际上,on_closing函数甚至没有被调用:“OVER”从不被打印。你有解决方案的想法吗? – Hugo