【问题标题】:How can I terminate a tkinter mainloop from a different thread如何从不同的线程终止 tkinter 主循环
【发布时间】:2022-01-22 12:42:13
【问题描述】:

我有以下问题:我想用 tkinter 创建一个 GUI,它对从套接字发送的信号作出反应。例如,我希望能够在收到 end 信号时终止应用程序。

为此,我有一个函数,在一个单独的线程中运行,它监听信号并采取相应的行动。但是,当我尝试销毁 tkinter-GUI 时,程序会停止,并给出以下错误消息:

致命的 Python 错误:PyEval_RestoreThread:函数必须在持有 GIL 的情况下调用,但 GIL 已释放(当前 Python 线程状态为 NULL) Python 运行时状态:已初始化

我重新创建了这个最小的工作示例,给出了相同的行为:

import tkinter as tk
import time
import threading


class Gui(tk.Frame):
    """Minimal GUI with only a button"""
    def __init__(self, master: tk.Tk):
        tk.Frame.__init__(self, master)
        self.pack()
        tk.Button(self, text='Spam').pack()


class Client:
    """Client for handling signals"""
    def __init__(self, master: tk.Tk):
        self.master = master
        self.gui = Gui(self.master)
        self.signal = None  # Initialize signal
        self.thread = threading.Thread(target=self.listen_thread)
        self.running = True
        self.thread.start()

    def listen_thread(self):
        """Listen for signals and handle actions"""
        while self.running:
            signal = self.signal  # Dummy signal, set by external method, instead of received message from socket
            if signal == 'end':  # End signal received
                self.master.destroy()  # Destroy tkinter GUI, error occurs here
                self.running = False  # Terminate while loop
            else:
                time.sleep(0.2)


def send_signal_after(receiver: Client, delay: float = 2.0):
    """Send a signal to the client after short delay"""
    time.sleep(delay)
    receiver.signal = 'end'


if __name__ == '__main__':
    root = tk.Tk()
    client = Client(root)
    threading.Thread(target=send_signal_after, args=(client,)).start()
    root.mainloop()
    if client.thread:  # Check if thread is still running, if so, wait for termination
        client.thread.join()

我在 MacOS 12.1、Python 3.10 上运行它。

还有其他方法可以终止应用程序吗?我知道,我可能可以使用 sys.exit(),但我想以一种更简洁的方式来执行此操作。

谢谢!

【问题讨论】:

    标签: python multithreading tkinter


    【解决方案1】:

    所以,为了理解怎么做,我举了一个例子:

    这是第一个文件(主要文件):

    import tkinter as tk
    from tkinter import *
    import threading
    import file2 as file2
    
    def func(gui):
       # just some code around here
    
    # start up the program
    root = Tk()
    
    # pass the root in the __init__ function from file2
    mainGui = file2.file2Class(root)
    
    # Start the new thread
    theThread = threading.Thread(target=func, args=([mainGui]))
    theThread.daemon = True
    theThread.start()
    
    # loop command
    tk.mainloop()
    

    这是文件 2:

    import tkinter as tk
    from tkinter import *
    
    class file2Class:
        root = None
        def __init__(self, initialRoot):
            self.root = initialRoot
            self.createUI();
    
        def createUI(self):
            # This is an exit button
            tk.Button(
                self.root, 
                text="Exit",
                font = "Verdana 10 bold", 
                fg="red",
                command=self.root.destroy, # <- this is the exit command
                width=25,
                height=2).grid(row=0,column=0)
    

    所以,最重要的是确保在线程的args 中传递root

    希望对你有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-20
      • 2017-05-23
      • 2019-01-27
      • 1970-01-01
      • 2021-03-24
      相关资源
      最近更新 更多