【问题标题】:Tkinter Loop Never Exits CleanlyTkinter 循环永远不会干净地退出
【发布时间】:2016-07-31 18:56:52
【问题描述】:

Python 2.7

我为我的 Tkinter GUI 编写了一个 run 方法,而不是使用标准的 mainloop,当我关闭窗口时它总是退出错误,即使在按照 SO 其他地方的建议实施 WM_DELETE_WINDOW 协议之后也是如此.我尝试在协议回调中调用exit 并从循环中调用returning,但Python 总是最后一次通过循环。这是为什么呢?

class FrameApp(object):
    def __init__(self):
        ...
        self.rootWin.protocol("WM_DELETE_WINDOW", self.callback_destroy)
        self.winRunning = False

    def callback_destroy(self):
        self.winRunning = False
        self.rootWin.destroy() # go away, window
        exit() # GET OUT

这是运行循环:

    def run(self):
        last = -infty
        self.winRunning = True
        ...
        while self.winRunning:
            # 4.a. Calc geometry
            self.calcFunc( self.get_sliders_as_list() )
            # 4.b. Send new coords to segments
            self.simFrame.transform_contents()
            # 4.d. Wait remainder of 40ms
            elapsed = time.time() * 1000 - last
            if elapsed < 40:
                time.sleep( (40 - elapsed) / 1000.0 )
            # 4.e. Mark beginning of next loop
            last = time.time() * 1000
            # 4.f. Update window
            if not self.winRunning: # This does not solve the problem 
                return # still tries to call 'update', 
                #        and never exits cleanly
            self.canvas.update() 
            # don't know how to prevent these from being called 
            # again after the window is destroyed
            self.rootWin.update_idletasks()

结果:

文件“/usr/lib/python2.7/lib-tk/Tkinter.py”,第 972 行,在 更新空闲任务 self.tk.call('update', 'idletasks') _tkinter.TclError:无法调用“更新”命令:应用程序已被销毁

【问题讨论】:

  • 你为什么不用tkinter的mainloop
  • 我没有使用它,因为我有一些计算要在循环之外进行(self.calcFunc,在实例化 FrameApp 的文件中)并且它们还不够重他们自己的线程。 ~ 我正在尝试使这个模块化,以便FrameApp 可以运行任何分配给它的calcFunc
  • 您的循环需要内部中断(所以如果完成),如何管理所有标志?意思是while True : if my_end_signal occured : break。在销毁一切之前检测end point
  • 可以看到循环中有if not self.winRunning子句。当窗口销毁协议将运行标志属性设置为 false 时,它​​应该中断循环。我已尝试将 returnbreak 作为对退出标志的响应,但在这两种情况下我都会遇到相同的错误。
  • 我正在使用python FILENAME.py 直接从终端调用脚本。如果我从它启动 Tkinter 脚本,我常用的 IDE Spyder 有时会导致创建许多窗口。

标签: python python-2.7 tkinter tkinter-canvas event-loop


【解决方案1】:

没有主循环,tkinter 无法获取WM_DELETE_WINDOW 消息来调用您的退出函数。 (或者更确切地说,它只能在 update_idletasks 调用的〜毫秒期间捕获任何东西,因为它不会排队,因为 tkinter 没有事件循环(因此排队),因为你从未启动过。)它可以'如果不能与窗口管理器(系统)通信就不能捕获信号,如果不循环就不能通信。

要解决它,只需使用事件/主循环。让你的 run 函数保存它需要的任何状态,并在你希望的任何时间间隔内调用自己 after


另一方面,不要将time.sleep 与 tkinter 一起使用——它会阻止它执行任何操作(而且剩余的 40 毫秒睡眠可能比循环的其余部分长,因此您将有 41 毫秒的等待时间和 0.5 毫秒的可点击性)。相反,只需仔细配置您的 root.after 语句(您也可以计算其中的内容)

【讨论】:

  • 你是说这种味道吗? def tick(self): calcTime = remaining_t() ; root.after( calcTime , self.tick )
  • 完全符合这些原则。甚至可以是 def tick(self): root.after(remaining_t(), self.tick) ;如果没有在其他地方使用,则无需存储中间变量。
  • 这解决了!我不知道 Tkinter 窗口的 after 方法。 ~ 我遇到的两个解决方案是将所有计算打包在一个小部件回调中(丑陋)或在单独的线程中运行计算(对于玩具脚本来说工作量太大)。
猜你喜欢
  • 2012-09-14
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多