2013-11-23 71 views
2

我有一個tkinter應用程序(作爲主線程運行),在它內部打開新的頂層窗口 - 它是測試的日誌窗口打印結果(測試使用硒的webdriver)。該對話框也是所有測試的調用者。從Tkinter運行線程並等待完成

所以我想要顯示對話框(作爲頂層,整個應用程序還有一個窗口),運行測試,等待測試完成並打印結果,然後對另一個測試單元執行相同操作。但我不想在測試過程中讓窗口凍結。

我試過使用線程,但它顯然可以像這樣工作。在這種情況下,直到測試完成纔會啓動對話框。

這裏是對話窗口的代碼。

testDialog = TestDialog(self.parent, self._webtester, url) 

謝謝你的任何忠告:

class TestDialog(tkinter.Toplevel): 

    def __init__(self, parent, tester, url): 
     super().__init__(parent)   
     self.parent = parent 
     self.webtester = tester; 

     self.__initComponents() 

     self.run(url)    

     self.wait_window(self) 

    def __initComponents(self): 
     self.transient(self.parent) 

     frame = tkinter.Frame(self) 

     self._tarea = tkinter.Text(frame, state='disabled',wrap='none', width=55, height=25) 

     vsb = tkinter.Scrollbar(frame, orient=tkinter.VERTICAL, command=self._tarea.yview) 
     self._tarea.configure(yscrollcommand=vsb.set) 


     self._tarea.grid(row=1, column=0, columnspan=4, sticky="NSEW", padx=3, pady=3) 
     vsb.grid(row=1, column=4, sticky='NS',pady=3) 
     frame.grid(row=0, column=0, sticky=tkinter.NSEW) 

     frame.columnconfigure(0, weight=2) 
     frame.rowconfigure(1, weight=1) 

     window = self.winfo_toplevel() 
     window.columnconfigure(0, weight=1) 
     window.rowconfigure(0, weight=1) 

     self.bind("<Escape>", self.close) 

     self.protocol("WM_DELETE_WINDOW", self.close) 
     self.grab_set() 

    def appendLine(self, msg): 
     self._tarea['state'] = 'normal' 
     self._tarea.insert("end", msg+'\n') 
     self._tarea['state'] = 'disabled' 

    def run(self, url): 

     self.appendLine("Runneing test #1...") 

     try: 
      thr = threading.Thread(target=self.webtester.urlopen, args=(url,)) 
      thr.start() 
     except: 
      pass 

     thr.join() 

     self.webtester.urlopen(url) 

     self.appendLine("Running test #2")   
     try: 
      thr = threading.Thread(target=self.webtester.test2) 
      thr.start() 
     except: 
      pass   

    def close(self, event=None): 
     self.parent.setBackgroundScheme(DataTreeView.S_DEFAULT) 
     self.parent.focus_set() 
     self.destroy() 

此對話框是從父窗口只需打開。

回答

3

爲防止GUI凍結,您需要快速結束self.run()。 它需要產生一個線程,啓動線程,然後結束:

import Queue 
sentinel = object() 
root = tkinter.Tk() 

... 
def run(self, url): 
    outqueue = Queue.Queue() 
    thr = threading.Thread(target=self.run_tests, args=(url, outqueue)) 
    thr.start() 
    root.after(250, self.update, outqueue) 

現在這個線程運行時可以很長時間運行函數:

def run_tests(self, url, outqueue): 
    outqueue.put("Running test #1...") 
    self.webtester.urlopen(url) 
    outqueue.put("Running test #2")   
    self.webtester.test2() 
    outqueue.put(sentinel) 

但因爲Tkinter的期望所有GUI調用源自單個線程,此衍生線程不得進行任何GUI調用。對於它與GUI交互,你可以通過一個Queue.Queue發送輸出(如狀態更新消息),並同時讓主線程Tkinter的定期監測這Queue.Queue(通過調用root.after):

def update(self, outqueue): 
    try: 
     msg = outqueue.get_nowait() 
     if msg is not sentinel: 
      self.appendLine(msg) 
      root.after(250, self.update, outqueue) 
     else: 
      # By not calling root.after here, we allow update to 
      # truly end 
      pass 
    except Queue.Empty: 
     root.after(250, self.update, outqueue) 
+0

我已經我自己想出了一些非常相似的東西,但是你的解決方案更加優雅,並且幫助我理解了這個問題。謝謝你,先生! – dakov