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

时间:2014-09-28 11:43:03

标签: python multithreading tkinter

我正在尝试设置一个tkinter窗口,我可以使用队列在主循环外部进行交互。当我在spyder中解释这个时,它运行正常。在启动()之后,我的Tk窗口出现了,我仍然有控制台访问权限,允许我改变窗口的标题来改变窗口的标题。

但是,关闭窗口是个问题。它关闭很好,检查mythread显示线程确实停止了。但是再次调用launch()不会做任何事情并阻止解释器。然后我被迫重启python :(

是否有需要清洁的东西阻止我创建新线程?从我在这里读到的内容来看,tkinter不喜欢不在main中运行,这就是我在这里所做的。但为什么第一个实例呢?

我希望能够编写一些低级函数,例如下面的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()

1 个答案:

答案 0 :(得分:-1)

我相信在这段代码中使用全局变量只是从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。