2014-09-28 90 views
1

我想設置一個tkinter窗口,我可以使用隊列與主循環外部進行交互。當我用spyder解釋這個時,它工作正常。在啓動()之後,我的Tk窗口出現,並且我仍然有控制檯訪問權限,允許我change_titre('whatever')來更改窗口的標題。使用線程在tkinter中保持控制檯控制

但是,關閉窗口是一個問題。它關閉正常,檢查mythread表明線程確實停止。但是,再次調用launch()將不會執行任何操作並阻止解釋器。我然後被迫重新啓動蟒蛇:(

有什麼需要清潔,防止我創建一個新的線程?從我在這裏看到的東西,tkinter不喜歡不在主要運行,這是什麼我在這裏做的,但爲什麼第一個實例工作呢?

我想能夠編寫一些像下面的change_titre這樣的低級函數(例如繪製基本的東西),然後允許用戶如果全部失敗,是否還有其他方法?

import tkinter as tk 
from threading import Thread 
#import threading 
import queue 

request_queue = None 
master = None 
mythread = None 

def submit_to_tkinter(callable, *args, **kwargs): 
    request_queue.put((callable, args, kwargs)) 
    return 

def threadmain(): 
    global master 
    master = tk.Tk() 
    master.title("stuff") 
    drawzone = tk.Canvas(master, width=300, height = 300, bg='white') 
    drawzone.pack() 
    def queueloop(): 
     try: 
      callable, args, kwargs = request_queue.get_nowait() 
     except queue.Empty: 
      pass 
     else: 
      callable(*args, **kwargs) 
     master.after(500, queueloop) 
    queueloop() 
    master.mainloop() 

def change_titre(text): 
    submit_to_tkinter(master.title,text) 
    return 


def launch(): 
    global mythread,request_queue 
    request_queue = queue.Queue() 
    mythread = Thread(target=threadmain, args=()) 
    mythread.daemon=True 
    mythread.start() 
+0

第一個實例不適用於我,並導致python直接段錯誤。正如你所說,你不應該在主線程中的任何線程中運行Tkinter。這段代碼的目的是什麼,你想要它做什麼? – ebarr 2014-09-29 12:21:26

+0

這是用於教學目的:我試圖讓學生使用我給他們的低級函數創建自己的函數。 – imj 2014-09-30 19:29:04

回答

-1

我相信在這個cod中使用全局變量e是從Python REPL訪問數據的簡單方法(因爲代碼應該用於教學目的)。但是將Tk對象保存在全局變量中並從不同線程訪問它是這個問題的根源。

我認爲在每次新啓動時將master(全局變量)設置爲新的Tk對象可能有所幫助。因此,我們可以確定當launch()完成時,以前的Tk對象是垃圾回收,並且它的線程已連接。

這裏是改變的功能(註釋顯示哪些部分被改變)。

# import garbage collection module 
import gc 

def threadmain(): 
    global master 
    master = tk.Tk() 
    master.title("stuff") 
    drawzone = tk.Canvas(master, width=300, height = 300, bg='white') 
    drawzone.pack() 
    def queueloop(): 
     try: 
      callable_, args, kwargs = request_queue.get_nowait() 
     except queue.Empty: 
      pass 
     else: 
      callable_(*args, **kwargs) 
     master.after(500, queueloop) 
    queueloop() 
    master.mainloop() 
    # added these 2 lines to remove previous Tk object when thread is finished 
    master = None 
    gc.collect() 

def launch(): 
    global mythread,request_queue 
    # added these 3 lines to end previous open thread if any 
    if mythread and mythread.isAlive(): 
     submit_to_tkinter(master.destroy) 
     mythread.join() 
    request_queue = queue.Queue() 
    mythread = Thread(target=threadmain, args=()) 
    # no need for daemon threads 
    # mythread.daemon=True 
    mythread.start() 

現在每次調用launch()將關閉以前Tk的窗口,等待線程的加入,在一個新的線程打開一個新Tk的面前。

+0

我改編了python 3(print語句)的代碼,但不幸的是這並沒有解決問題。第二個電話仍然凍結。此外,我認爲將主人設置爲新的傳統知識對象正是我所做的。不管怎麼說,還是要謝謝你 ! – imj 2014-10-01 18:43:11

+0

調用它的threadmain並不會成爲主線程,但該解決方案仍然存在與OP解決方案相同的問題。查看http://stackoverflow.com/questions/10556479/running-a-tkinter-form-in-a-separate-thread等問題,討論爲什麼當Tkinter不在主線程中時不工作 – ebarr 2014-10-01 23:42:30

+0

@imj第一次我在Python 2.7.6(因此是print語句)上測試了它,看到您的評論後,我也在Python 3.4上測試了它。多次調用launch()將打開一個新窗口(如果有的話關閉前一個窗口)而不凍結。我正在測試x64 Linux機器。 – farzad 2014-10-02 01:32:28