使用线程在tkinter中保持控制台控制



我正在尝试设置一个tkinter窗口,使用队列可以在主循环之外与之交互。当我用spyder解释这个时,它很好用。在启动()之后,我的Tk窗口出现了,我仍然可以访问控制台,允许我更改itre('hatever')来更改窗口的标题。

然而,关闭窗口是一个问题。它关闭得很好,检查mythread显示线程确实停止了。但是,再次调用launch()将不起任何作用,并阻塞解释器。然后我被迫重新启动python:(

是否有什么东西需要清理,从而阻止我创建新线程?从我在这里读到的情况来看,tkinter不喜欢不被主控,这就是我在这里所做的。但为什么第一个例子有效呢?

我希望能够编写一些低级函数,如下面的change_titre(例如绘制基本内容),然后允许用户使用这些函数编写自己的函数。如果一切都失败了,还有别的路要走吗?

import tkinter as tk
from threading import Thread
#import threading
import queue
request_queue = None
master = None
mythread = None
def submit_to_tkinter(callable, *args, **kwargs):
    request_queue.put((callable, args, kwargs))
    return
def threadmain():
    global master
    master = tk.Tk()
    master.title("stuff")
    drawzone = tk.Canvas(master, width=300, height = 300, bg='white')
    drawzone.pack()
    def queueloop():
        try:
            callable, args, kwargs = request_queue.get_nowait()
        except queue.Empty:
            pass
        else:
            callable(*args, **kwargs)
        master.after(500, queueloop)
    queueloop()
    master.mainloop()
def change_titre(text):
    submit_to_tkinter(master.title,text)
    return

def launch():
    global mythread,request_queue
    request_queue = queue.Queue()
    mythread = Thread(target=threadmain, args=())
    mythread.daemon=True
    mythread.start()

我相信在这段代码中使用全局变量只是从Python REPL访问数据的一种简单方法(因为这段代码应该用于教学目的)。但是,将Tk对象保留在全局变量中并从不同的线程访问它是这个问题的根源。

我认为在每次新发布时将master(全局变量)设置为新的Tk对象可能会有所帮助。因此,我们可以确保在launch()完成并加入它的线程时,前一个Tk对象被垃圾收集。

以下是更改后的功能(注释显示更改了哪些部分)。

# import garbage collection module
import gc
def threadmain():
    global master
    master = tk.Tk()
    master.title("stuff")
    drawzone = tk.Canvas(master, width=300, height = 300, bg='white')
    drawzone.pack()
    def queueloop():
        try:
            callable_, args, kwargs = request_queue.get_nowait()
        except queue.Empty:
            pass
        else:
            callable_(*args, **kwargs)
        master.after(500, queueloop)
    queueloop()
    master.mainloop()
    # added these 2 lines to remove previous Tk object when thread is finished
    master = None
    gc.collect()
def launch():
    global mythread,request_queue
    # added these 3 lines to end previous open thread if any
    if mythread and mythread.isAlive():
        submit_to_tkinter(master.destroy)
        mythread.join()
    request_queue = queue.Queue()
    mythread = Thread(target=threadmain, args=())
    # no need for daemon threads
    # mythread.daemon=True
    mythread.start()

现在,每次调用launch()时,它都会关闭上一个Tk窗口,等待线程加入,然后在新线程中打开新的Tk。

相关内容

最新更新