2016-02-14 29 views
0

我有一個python程序正在爲客戶端抓取網頁數據。 tkinter用於界面。大綱是:如何處理tkinter主循環中的錯誤?

  1. 窗口1讓用戶選擇要刮的信息。
  2. 窗口1關閉
  3. 刮刀啓動單獨的線程。這個線程反過來會產生更多的線程,以允許一次下載多個頁面。
  4. 打開窗口2以顯示下載進度(例如,「下載17的客戶端5」)
  5. 用戶關閉窗口2以結束程序。

程序將第一個幾百頁的工作,但隨後開始吐出錯誤消息:直到所有線程都已經停止

Traceback (most recent call last): 
File "C:\Users\Me\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 248, in __del__ 
if self._tk.getboolean(self._tk.call("info", "exists", self._name)): 
RuntimeError: main thread is not in main loop 
Exception ignored in: <bound method Variable.__del__ of <tkinter.IntVar object at 0x03245510>> 

多次。不知道什麼可能導致這個錯誤。實際的代碼是:

import scraper, threading 
import tkinter as tk 
from queue import Queue 

outputQueue = Queue() 

class OutputRedirect(object): 
    def __init__(): 
     super().__init__() 

    def write(self, string): 
     outputQueue.put(string) 

def getInformation(): 
    stdout = sys.stdout 
    sys.stdout = OutputRedirect() 

    scraper.startThreads() 
    scraper.startPulling() 

    sys.stdout = stdout 

def updateTextField(window, root): 

    if not outputQueue.empty(): 
     string = outputQueue.get() 
     window.textArea.insert("insert", string)   
     outputQueue.task_done() 

    root.after(1, updateTextField, window, root) 

'''widget/window definitions - not important''' 

guiInfo = {"stuff1": [], "stuff2": []} 

root = tk.Tk() 
window1 = Window1(root, guiInfo) 
window1.mainloop() 

pullThread = threading.Thread(target=pullClaims, 
           args=(list(guiInfo["stuff1"]), 
           list(guiInfo["stuff2"])), daemon=True) 
pullThread.start() 

root = tk.Tk() 
window2 = Window2(root) 
root.after(0, updateTextField, window2, root) 
window2.mainloop() 

刮板程序(對自己工作正常)使用打印報表的用戶反饋。我只是將stdout指向隊列,而不是重寫所有內容。主線程使用「after」函數每秒鐘檢查隊列幾次。如果其中有任何內容,則會將其打印到窗口上的文本窗口小部件中。

我已經把代碼中的任何地方嘗試/抓住,但他們沒有抓到一件事。我確信問題出在mainloop本身,但我找不到任何有關如何在其中添加新內容的最新信息。任何幫助將不勝感激。

+1

我看到一些失蹤進口(如'threading'),你好像一個'主循環後有東西()'調用,包括_second_'主循環()',這是隻是壞消息。 – TigerhawkT3

+0

我的不好。這實際上是代碼的簡化版本,其中一些變量名稱已更改,並且只列出了必要的導入語句以提高可讀性。在實際的代碼中,線程被導入。此外,第二個主循環出現在第二個窗口上,並且只有在第一個窗口關閉的情況下。 – TripleD

回答

0

要處理Tkinter的錯誤,你就以下

class TkErrorCatcher: 

    ''' 
    In some cases tkinter will only print the traceback. 
    Enables the program to catch tkinter errors normally 

    To use 
    import tkinter 
    tkinter.CallWrapper = TkErrorCatcher 
    ''' 

    def __init__(self, func, subst, widget): 
     self.func = func 
     self.subst = subst 
     self.widget = widget 

    def __call__(self, *args): 
     try: 
      if self.subst: 
       args = self.subst(*args) 
      return self.func(*args) 
     except SystemExit as msg: 
      raise SystemExit(msg) 
     except Exception as err: 
      raise err 

import tkinter 
tkinter.CallWrapper = TkErrorCatcher 

但在你的情況,請不要這樣做。這應該只在想要在生產時間隱藏來自用戶的錯誤消息的情況下完成。如上所述,你有一個nono正在進行。 產卵多個窗口,你可以使用tkinter.Toplevel

我建議一般讀

,並在Tkinter的你線程的特殊問題這篇博客文章釘了它。基本上你需要讓tkinter mainloop阻塞程序主線程,然後在來自其他線程的調用之後在mainloop中運行其他代碼。

+0

它工作! 有趣的是,我最初一直在使用Toplevel,但遇到了「主循環」問題,因爲我不是多線程的。我錯誤地認爲「Toplevel」是問題,並用多個窗口的想法取而代之。我後來實現了多線程,但從未想過要回到使用Toplevel,直到你提出它。感謝您花時間發佈這些鏈接。 – TripleD

+0

很高興聽到它。你應該接受正確的答案。併發編程確實變得相當複雜,花時間玩鎖,其他一些可用選項絕對值得。併發性在GUI編程中經常出現,如果處理不當,會導致很難發現錯誤。 – timeyyy

+0

完成並完成。感謝您指出我需要接受答案。仍然是Stack Exchange的新手。 – TripleD