2016-07-31 78 views
0

的Python 2.7Tkinter的循環永遠不會乾淨地退出

我已經寫了run方法我Tkinter的GUI,而不是使用標準mainloop,它總是在錯誤退出,當我關閉窗口,甚至實施WM_DELETE_WINDOW協議後,正如其他地方建議的那樣。我嘗試在回調中調用exit,並且從循環中調用return,但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() 

結果:

File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 972, in update_idletasks self.tk.call('update', 'idletasks') _tkinter.TclError: can't invoke "update" command: application has been destroyed

+1

爲什麼你不使用tkinter的'mainloop'? –

+0

我沒有使用它,因爲我在循環之外做了一些計算('self.calcFunc',在實例化'FrameApp'的文件中),並且它們還不夠重,無法擁有自己的線程。 〜我試圖使這個模塊化,以便'FrameApp'可以運行任何分配給它的'calcFunc'。 – SquareCrow

+0

你的循環需要打破內部(所以如果完成),如何管理所有的標誌?意思是「while True:如果my_end_signal發生:break'。在破壞之前檢測'終點'! – dsgdfg

回答

1

沒有主循環,Tkinter的不能得到WM_DELETE_WINDOW消息打電話給你的退出功能。 (或者說,它只能在update_idletasks調用的〜毫秒內捕獲任何內容,因爲它不會排隊,因爲tkinter沒有事件循環(因此排隊),因爲你從不啓動它。)它不能如果它不能與窗口管理器(系統)進行通信,則會捕獲信號,並且如果它沒有循環,它將無法通信。

要解決它,只需使用事件/主循環。使您的run函數可以保存所需的任何狀態,並隨時調用它自己的after


在另一方面,不tkinter-它防止它做什麼用time.sleep(還有剩餘的睡眠40毫秒可能長於循環的其餘部分,所以你必須41毫秒等待和0.5毫秒的可點擊性)。相反,只要仔細配置你的root.after陳述(你也可以計算它們的東西)

+0

你的意思是這種味道? 'def tick(self): calcTime = remaining_t(); root.after(calcTime,self.tick)' – SquareCrow

+1

就是這樣。它甚至可以是「def tick(self):root.after(remaining_t(),self.tick)';如果不在其他地方使用,則不需要存儲中間變量。 – Delioth

+0

這解決了它!我不知道Tkinter窗口的'after'方法。 〜我遇到的兩種解決方案是將所有計算器都打包成一個小部件回調(醜陋)或者在一個單獨的線程中運行calc(對於玩具腳本來說工作太多)。 – SquareCrow