2014-11-02 89 views
11

我聽說Python中的線程不容易處理,他們變得更加糾結於tkinter。線程和tkinter python 3

我有以下問題。我有兩個類,一個用於GUI,另一個用於無限過程。首先,我啓動GUI類,然後啓動無限過程的類。我希望當你關閉GUI時,它也完成了無限的過程,程序結束。

代碼的簡化版本如下:

import time, threading 
from tkinter import * 
from tkinter import messagebox 

finish = False 

class tkinterGUI(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     global finish 
     #Main Window 
     self.mainWindow = Tk() 
     self.mainWindow.geometry("200x200") 
     self.mainWindow.title("My GUI Title") 
     #Label 
     lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20) 
     #Start 
     self.mainWindow.mainloop() 
     #When the GUI is closed we set finish to "True" 
     finish = True 

class InfiniteProcess(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     global finish 
     while not finish: 
      print("Infinite Loop") 
      time.sleep(3) 

GUI = tkinterGUI() 
GUI.start() 
Process = InfiniteProcess() 
Process.start() 

當我點擊關閉按鈕(在右上角)出現在控制檯以下錯誤:

Tcl_AsyncDelete :異步處理程序由錯誤的線程刪除

我不知道它爲什麼發生或它的意思,請幫助!

+0

您的簡歷版適用於我...必須有一些你忘記添加,這是導致你的問題 – mguijarr 2014-11-02 19:52:30

+0

@mguijarr我在谷歌閱讀,這個錯誤是更常見的窗口,你的SO?我的是Windows 7 x64。也許窗口是問題:/ – 2014-11-02 20:14:50

回答

10

All Tcl commands need to originate from the same thread。由於tkinter的 依賴於Tcl,通常必須使所有tkinter gui語句來自同一個線程。因爲 mainWindowtkinterGui線程中被實例化,但是 - 因爲mainWindowtkinterGui的屬性 - 直到tkinterGui在主線程中被銷燬才被銷燬。即改變self.mainWindowmainWindow -

該問題可以通過不使mainWindowtkinterGui 的屬性來避免。當run方法在tkinterGui線程中結束時,這允許mainWindow被銷燬。但是,往往就可以避免線程完全使用mainWindow.after調用來代替:

import time, threading 
from tkinter import * 
from tkinter import messagebox 

def infinite_process(): 
    print("Infinite Loop") 
    mainWindow.after(3000, infinite_process) 


mainWindow = Tk() 
mainWindow.geometry("200x200") 
mainWindow.title("My GUI Title") 
lbCommand = Label(mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20) 
mainWindow.after(3000, infinite_process) 
mainWindow.mainloop() 

如果要定義一個類裏面的圖形用戶界面,你還可以這樣做:

import time, threading 
from tkinter import * 
from tkinter import messagebox 

class App(object): 
    def __init__(self, master): 
     master.geometry("200x200") 
     master.title("My GUI Title") 
     lbCommand = Label(master, text="Hello world", 
          font=("Courier New", 16)).place(x=20, y=20) 

def tkinterGui(): 
    global finish 
    mainWindow = Tk() 
    app = App(mainWindow) 
    mainWindow.mainloop() 
    #When the GUI is closed we set finish to "True" 
    finish = True 

def InfiniteProcess(): 
    while not finish: 
     print("Infinite Loop") 
     time.sleep(3) 

finish = False 
GUI = threading.Thread(target=tkinterGui) 
GUI.start() 
Process = threading.Thread(target=InfiniteProcess) 
Process.start() 
GUI.join() 
Process.join() 

甚至更簡單,只需使用主線程來運行GUI主循環:

import time, threading 
from tkinter import * 
from tkinter import messagebox 

class App(object): 
    def __init__(self, master): 
     master.geometry("200x200") 
     master.title("My GUI Title") 
     lbCommand = Label(master, text="Hello world", 
          font=("Courier New", 16)).place(x=20, y=20) 

def InfiniteProcess(): 
    while not finish: 
     print("Infinite Loop") 
     time.sleep(3) 

finish = False 
Process = threading.Thread(target=InfiniteProcess) 
Process.start() 

mainWindow = Tk() 
app = App(mainWindow) 
mainWindow.mainloop() 
#When the GUI is closed we set finish to "True" 
finish = True 
Process.join() 
+0

非常感謝你!你是主人! – 2014-11-02 20:17:29

+0

但有時你需要將mainWindow作爲一個屬性,例如,如果你想使用: self.mainWindow.protocol(「WM_DELETE_WINDOW」,self。退出) 當你定義的函數退出()和你寫的: self.mainWindow.destroy() self.mainWindow.quit() 主窗口必須是一個屬性,否則該函數退出將無法識別主窗口。 – 2014-11-02 20:41:22

+0

你仍然可以使用一個類;只是不要使該實例成爲'threading.Thread'的一個屬性。我已經在上面添加了一些代碼來提示如何。 – unutbu 2014-11-02 20:57:18

0

此處的修復很簡單,但很難發現:

呼叫mainWindow.quit()後立即mainwindow.mainloop(),使清理髮生在同一個線程創建了TK UI,而不是主線程時蟒蛇退出上一個上。

+1

這似乎不是一個通用的解決方案。我的代碼展示了同樣的問題(Tcl_AsyncDelete錯誤),並在'mainWindow.mainloop()'後面添加'mainWindow.quit()'沒有效果。 – 2017-03-03 22:19:01

+0

@BryanOakley是的,同樣的問題:在試圖避免Tcl_AsyncDelete我發現'.quit()'在Python 2.7.14中是不必要的,而在Python 3.6.3中是無效的。還沒有解決方案。 – jez 2017-11-06 19:19:25