【问题标题】:Quitting Tkinter app from menu bar - can't kill thread从菜单栏中退出 Tkinter 应用程序 - 无法杀死线程
【发布时间】:2014-05-08 22:31:14
【问题描述】:

我有一个带有 Tkinter GUI 的 Python 应用程序。在应用程序中,用户调用一个长时间运行的后台进程(作为来自 Python 的 threading 模块的 Thread 实现)。如果我在程序完成之前退出程序,我将无法杀死后台线程。 如果我通过顶部角落的“X”关闭根窗口来退出应用程序,我的代码可以工作,但如果我从顶级菜单栏退出(即 Python > Quit 或 Ctrl+Q)则不能强>。由于大多数应用程序都使用后者,我真的很想实现它。

现在,我使用如下代码杀死后台线程:

class BackgroundCallFrame(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.background_call = BackgroundCall()
        self.background_call.start()

class BackgroundCall(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self._stop_req = threading.Event()

    def run(self):
        for i in range(1,100000):
            if self._stop_req.is_set():
                return
            else:
                # do stuff

    def stop(self):
        self._stop_req.set();

def main():

    def kill_all_threads():
        if child.background_call is not None:
           child.background_call.stop()
           child.background_call.join()
        root.destroy()

    root = Tk()
    root.wm_protocol ("WM_DELETE_WINDOW", kill_all_threads)

    child = BackgroundCallFrame()
    child.pack()
    root.mainloop()

if __name__ == '__main__':
    main()

如果我在没有先明确关闭根窗口的情况下退出程序,我该如何完成这项工作?

我相信我的问题是当我调用 Ctrl+Q 时没有调用 kill_all_threads(),因为我添加到其中的任何打印语句都不会出现在控制台中。

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    通常你会想通过在该线程中设置一个变量来终止该线程以指示它停止工作。在线程的 run 方法中,您需要定期检查该变量以了解何时退出 run 方法。这是一个例子......

    import threading
    import time
    
    class WorkerThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.do_stop = False
    
        def run(self):
            while True:
                time.sleep(.5)
    
                print 'I am running'
    
                if self.do_stop:
                    return # exit loop and function thus exiting thread
    
    t = WorkerThread()
    t.start()
    
    time.sleep(4) # sleep while our thread runs and prints to the command line
    
    print 'setting do_stop to True'
    t.do_stop = True # signal to thread to stop
    
    t.join() # wait for thread to exit
    
    print 'all done'
    

    ...输出...

    I am running
    I am running
    I am running
    I am running
    I am running
    I am running
    I am running
    setting do_stop to True
    I am running
    all done
    

    【讨论】:

    • 抱歉,没有澄清 - 在child.background_call.stop() 中,我要求我的线程停止使用t._stop_req.set(),如对这个 SO 问题的第一个答案所述:stackoverflow.com/questions/11938109/…。我不确定这种方法是否会有所不同。我的方法大部分时间都有效,只是在从顶级菜单栏退出时无效。
    • 您链接的那个答案对我来说看起来不正确。最重要的是因为线程是用 .run() 启动的……它应该用 .start() 启动。这可能是您遇到的核心问题。此外 Threading.Event 用于在线程之间进行通信,而不是发出退出信号。该链接的答案也不起作用,因为它在 MyThread 的方法定义中包含错误(它们不引用 self)。
    • 非常感谢您的跟进。我并不是说我的整个代码在链接的答案中看起来像那样,只是我还使用了Event 来通知我的后台线程退出。这可能有点矫枉过正,但我​​很确定这不是我的问题,因为当根窗口关闭时后台线程确实会死掉。相反,我认为问题在于退出程序时没有调用我的kill_all_threads 回调。
    • 我应该问一个不同的、更简单的问题——我已经把它贴在这里了:stackoverflow.com/questions/23571888/…。再次感谢。
    猜你喜欢
    • 2018-11-26
    • 1970-01-01
    • 2013-09-16
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多