Python 在后台从另一个类运行函数

Python 在后台从另一个类运行函数,python,multithreading,tkinter,Python,Multithreading,Tkinter,我有一个工人班 class Parser: #(threading.Thread) ... def longTask(self,...) ... return ... 还有一个窗口类,它使用tkinter class Window: ... def startProcess(self,...): p = Parser() t= threading.Thread(target=p.longTa

我有一个工人班

class Parser: #(threading.Thread)
    ...
    def longTask(self,...)
        ...
        return
    ...
还有一个窗口类,它使用tkinter

class Window:
    ...
    def startProcess(self,...):
        p = Parser()
        t= threading.Thread(target=p.longTask())
        t.start()
        return
    ...
但是,即使longTask()在单独的线程中运行,我的GUI也会冻结——为什么

我的目标是在longTask()运行时更新窗口中的progressbar。longTask()在内部更新一个数字,范围从0到100

问题:在longTask()运行时,如何实现这一点并防止GUI冻结

注意:显然,当我调用t.start()时线程不会启动,但是当我通过t=threading.thread…

启动线程时,虽然注释中的解决方案可能有效,但我发现在过去,threading确实与tkinter不一致,tkinter有自己的虚拟事件管理器。我写这篇文章是为了,如果你尝试的东西不起作用,你应该能够以这种方式实现它

import tkinter
import tkinter.ttk
import threading
import queue
import time

def long(steps):
    time.sleep(1)
    for i in range(20):
        steps.put(5)
        time.sleep(0.2)

class Window:

    def __init__(self):
        self.steps = queue.Queue()

        self.root = tkinter.Tk()
        self.root.title("Loading")
        self.progressbar = tkinter.ttk.Progressbar(self.root)
        self.progressbar.pack(fill="both", padx=10, pady=10)

    def update(self):
        if self.progressbar["value"] == 100:
            self.root.quit()
            self.root.destroy()
        elif not self.steps.empty():
            step = self.steps.get()
            self.progressbar["value"] += step  # Not progressbar.step because it loops
        self.root.after(100, self.update)

    def main(self):
        self.update()
        self.root.mainloop()


window = Window()
t = threading.Thread(target=long, args=(window.steps,))
t.start()
window.main()
当试图让线程对象与tkinter对象进行交互时,使用某种可供两者使用的可变介质效果更好。因为增加加载条的步骤数可以改变,所以我使用了一个队列(它也是线程安全的)。每次发生某些事情时,函数都会将步骤放入队列中,tkinter应用程序有自己的循环来检查是否有任何新步骤,从而有效地调节线程和tkinter之间的交互


这只是防止GUI冻结的另一种方法,而不是修复现有算法的方法。

t是驻留在堆栈上的实例,因此在函数调用后,它将尝试调用该对象的释放,这是不可能的。因此,您可以将线程设置为一个成员,比如self.t=threading.thread(target=p.longstask())我已经包含了您的建议,但是为什么在t=threading.thread(…而不是在t.start()上调用longstask呢?应该是
p.longstask
而不是
p.longstask()
这也是p.longstask在创建线程之前立即启动,GUI冻结的原因。通常情况下,您只需将函数的引用传递给线程。但您调用函数(使其启动)。将其更改为self.t=threading.thread(target=p.longstask,args=(myArg))