簡體   English   中英

在 TKinter GUI 中使用線程

[英]Using Thread in TKinter GUI

我正在研究 Tkinter 桌面應用程序項目。 當我需要使用運行后端代碼的線程時,我開始磕磕絆絆。 我知道為了跨線程共享變量,我們應該使用全局變量。 下面是最小的代碼。

obj = None

class Frame:
    def __init__(self, frame):
        self.middle_frame = frame
        self.start_button = ttk.Button(self.middle_frame, text='Start', command=self.start)
        self.start_button.grid(row=0, column=0)
        self.stop_button = ttk.Button(self.middle_frame, text='Stop', command=self.stop)
        self.stop_button.grid(row=0, column=1)
        self.stop_button.config(state='disabled')

    def start(self):
        self.thread = threading.Thread(target=self.start_connection)
        self.thread.start()
        self.start_button.config(state='disabled')
        self.stop_button.config(state='normal')
    
    def start_connection(self):
        global obj
        obj = MainManager() # Starts the Backend Loop


    def stop(self):
        global obj
        obj.close_connection() # Want to break the loop here
        self.thread.join()


        self.stop_button.config(state='disabled')
        self.start_button.config(state='normal')
        

運行此代碼時,我得到obj.close_connection() AttributeError:'NoneType' object has no attribute 'close_connection' 但我期待 obj 成為 MainManager() 的對象。 我哪里錯了? 幫我解決這個問題。

您的代碼問題與tkinter或真正與多線程無關。 問題是MainManager__init__方法中啟動了循環,如下所示:

class MainManager:
    def __init__(self):
        self.alive = True

        # this part runs until stopped
        self.start_doing_stuff()

    def start_doing_stuff(self):
        while self.alive:
            sleep(1)
            print('doing stuff')

    def stop(self):
        self.alive = False

這是一個與您的錯誤類似的代碼:

from threading import Thread
from time import sleep

obj = None


class MainManager:
    def __init__(self):
        self.alive = True

        self.start_doing_stuff()

    def start_doing_stuff(self):
        while self.alive:
            sleep(1)
            print('doing stuff')

    def stop(self):
        self.alive = False


def test():
    global obj
    print('about to assign to my_global')
    obj = MainManager()
    print('assigned to my_global')


print(f'{obj=}')
t = Thread(target=test)
t.start()
sleep(3)
print(f'{obj=}')
obj.stop()
t.join()

由於__init__方法在調用stop方法之前不會終止,因此語句obj = MainManager()MainManager()的調用永遠不會終止,因此不會將obj分配給。 這意味着當obj.stop()被調用時, obj仍然是None

這可以通過我使__init__方法終止,並將長時間運行的代碼放入稍后調用的單獨方法中來解決:

from threading import Thread
from time import sleep

obj = None


class MainManager:
    def __init__(self):
        self.alive = True

    def start_doing_stuff(self):
        while self.alive:
            sleep(1)
            print('doing stuff')

    def stop(self):
        self.alive = False


def test():
    global obj
    print('about to assign to my_global')
    obj = MainManager()
    print('assigned to my_global')
    obj.start_doing_stuff()


print(f'{obj=}')
t = Thread(target=test)
t.start()
sleep(3)
print(f'{obj=}')
obj.stop()
t.join()

您是否導入了 MainManager,是否運行了 start_connection()?

此外,您將分配委托給一個線程。 您確定這會影響與主線程相同的obj嗎? 也許主線程保持“無”並且委托更改了副本中的值。 添加日志或打印語句並確保obj被分配了MainManager()對象並打印該對象的內存位置。 在主類中也打印obj變量的內存位置。 檢查它是否相同。

這是打印變量的內存地址的方式:

print(hex(id(your_variable)))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM