python - ループ - スレッドとtkinter



tkinter mainloop thread (2)

ここでの修正は簡単ですが、発見するのは難しいです:

mainWindow.quit() 直後に mainwindow.mainloop() 呼び出し て、クリーンアップがpythonの終了時のメインスレッドではなく、tk UIを作成したスレッドと同じスレッドで行われるようにします。

https://ffff65535.com

Pythonのスレッドは扱いが簡単ではなく、tkinterにもっと絡み合っていると聞きました。

次の問題があります。 2つのクラスがあります。1つはGUI用で、もう1つは無限プロセス用です。 まず、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: async handler deleted by the wrong thread

なぜ起こるのか、それが何を意味するのかはわかりません。


すべてのTclコマンドは、同じスレッドから発信する必要があります tkinter はTclに依存しているため、通常、すべての tkinter guiステートメントを同じスレッドから作成する必要があります。 この問題は、 mainWindowtkinterGui スレッドでインスタンス化されるために発生しますが、 mainWindowmainWindow の属性であるため、 mainWindow がメインスレッドで破棄されるまで tkinterGui されません。

この問題は、 mainWindowmainWindow の属性にしないこと、つまり self.mainWindowmainWindow 変更することで回避できます。 これにより、 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()

クラス内で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 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()




tcl