2014-09-28 94 views
1

我想设置一个tkinter窗口,我可以使用队列与主循环外部进行交互。当我用spyder解释这个时,它工作正常。在启动()之后,我的Tk窗口出现,并且我仍然有控制台访问权限,允许我change_titre('whatever')来更改窗口的标题。使用线程在tkinter中保持控制台控制

但是,关闭窗口是一个问题。它关闭正常,检查mythread表明线程确实停止。但是,再次调用launch()将不会执行任何操作并阻止解释器。我然后被迫重新启动蟒蛇:(

有什么需要清洁,防止我创建一个新的线程?从我在这里看到的东西,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() 
+0

第一个实例不适用于我,并导致python直接段错误。正如你所说,你不应该在主线程中的任何线程中运行Tkinter。这段代码的目的是什么,你想要它做什么? – ebarr 2014-09-29 12:21:26

+0

这是用于教学目的:我试图让学生使用我给他们的低级函数创建自己的函数。 – imj 2014-09-30 19:29:04

回答

-1

我相信在这个cod中使用全局变量e是从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的面前。

+0

我改编了python 3(print语句)的代码,但不幸的是这并没有解决问题。第二个电话仍然冻结。此外,我认为将主人设置为新的传统知识对象正是我所做的。不管怎么说,还是要谢谢你 ! – imj 2014-10-01 18:43:11

+0

调用它的threadmain并不会成为主线程,但该解决方案仍然存在与OP解决方案相同的问题。查看http://stackoverflow.com/questions/10556479/running-a-tkinter-form-in-a-separate-thread等问题,讨论为什么当Tkinter不在主线程中时不工作 – ebarr 2014-10-01 23:42:30

+0

@imj第一次我在Python 2.7.6(因此是print语句)上测试了它,看到您的评论后,我也在Python 3.4上测试了它。多次调用launch()将打开一个新窗口(如果有的话关闭前一个窗口)而不冻结。我正在测试x64 Linux机器。 – farzad 2014-10-02 01:32:28