0

我有一個程序,我一直在寫這個程序,開始作爲一個幫助函數,以便我根據該報告中的某些信息在共享驅動器上查找某個報告。我決定給它一個GUI,以便我可以將它分發給其他員工,並在我第一次嘗試實施tkinterthreading時遇到了幾個錯誤。摧毀一個線程中的Toplevel鎖定根

我知道老諺語「我有一個問題,然後我使用線程,現在我有兩個問題。」線程沒有,至少,解決第一個問題 - 在現在這樣第二....

我沖淡代碼:

class GetReport(threading.Thread): 
    def __init__(self,root): 
     threading.Thread.__init__(self) 

     # this is just a hack to get the StringVar in the new thread, HELP! 
     self.date = root.getvar('date') 
     self.store = root.getvar('store') 
     self.report = root.getvar('report') 
     # this is just a hack to get the StringVar in the new thread, HELP! 

     self.top = Toplevel(root) 
     ttk.Label(self.top,text="Fooing the Bars into Bazes").pack() 
     self.top.withdraw() 
    def run(self): 
     self.top.deiconify() 
     # a function call that takes a long time 
     self.top.destroy() #this crashes the program 

def main(): 
    root = Tk() 
    date,store,report = StringVar(),StringVar(),StringVar() 

    ##### 
    ## labels and Entries go here that define and modify those StringVar 
    ##### 

    def launchThread(rpt): 
     report.set(rpt) 

     # this is just a hack to get the StringVar in the new thread, HELP! 
     root.setvar('date',date.get()) 
     root.setvar('store',store.get()) 
     root.setvar('report',report.get()) 
     # this is just a hack to get the StringVar in the new thread, HELP! 

     reportgetter = GetReport(root) 
     reportgetter.start() 

    ttk.Button(root,text="Lottery Summary", 
       command=lambda: launchThread('L')).grid(row=1,column=3) 

    root.mainloop() 

我的預期輸出是root打開和填充與標籤,條目和按鈕(其中一些隱藏在此示例中)。每個按鈕都會從條目中提取數據並將它們發送到launchThread函數,該函數將創建一個新線程來執行所需的foos和條形圖以獲取我需要的文書。

該線程將啓動Toplevel,基本上只是通知用戶它正在處理它。當它完成後,頂層將關閉,我所要求的文書工作將打開(我使用ShellExecute打開一個.pdf),而線程退出(因爲它退出其run功能)

所發生的情況是,螺紋將啓動它的Toplevel,文檔將打開,那麼Python將變得不響應,需要「結束處理」。

回答

-1

Tkinter不能接受除主線程以外的線程的任何命令,因此在線程中啓動TopLevel將會因設計失敗,因爲它無法訪問其他線程中的Tk。爲了解決這個問題,請使用線程的.is_alive方法。

def GetReport(threading.Thread): 
    def __init__(self,text): 
     self.text = text 
     super().__init__() 

    def run(self): 
     # do some stuff that takes a long time 
     # to the text you're given as input 

def main(): 
    root = Tk() 
    text = StringVar() 

    def callbackFunc(text): 
     top = Toplevel(root) 
     ttk.Label(top,text="I'm working here!").pack() 
     thread = GetReport(text) 
     thread.start() 
     while thread.is_alive(): 
      root.update() # this keeps the GUI updating 
     top.destroy() # only when thread dies. 

    e_text = ttk.Entry(root,textvariable=text).pack() 
    ttk.Button(root,text="Frobnicate!", 
     command = lambda: callbackFunc(text.get())).pack() 
+0

這不是一個好的解決方案。你正在事件循環內部添加一個無限循環,這幾乎總是可以避免的。相反,你可以設置一個動畫循環來運行每秒鐘左右,以檢查線程是否存在。 –

+0

@BryanOakley你可以添加一個答案,證明我可以接受它嗎?這是我在經過不少研究和試驗和錯誤後提出的解決方案。 –

0

據我所知,你不能使用線程來改變任何GUI元素。比如摧毀一個Toplevel窗口。

任何Tkinter代碼都需要在程序的主循環中完成。

+0

嗨,詹姆斯。這是我一個月前提出的一個問題 - 自那以後我就解決了這個問題,但顯然忽略了發表答案!讓我去諮詢我的代碼,並記住我(確切地)我做了什麼。 –

+0

我有一個類似的問題,剛剛排序。它可能或可能不會幫助你,但在這裏。 http://stackoverflow.com/questions/21668538/closing-a-toplevel-window-from-a-seperate-thread-using-threading –

+0

感謝您的幫助。我的問題是通過下面的代碼解決的,'after'對我來說效果不好,因爲我的代碼可能需要一段時間才能完成。雖然我很欣賞這個想法! –