在写入时更新Tkinter文本小部件,而不是在类完成后更新

时间:2011-07-05 19:56:15

标签: python text tkinter buffer

我正在绑定,因为这是在分类机器上写的,我无法复制+粘贴在这里。作为一个新手,我的方法可能是非正统的。

我在Tkinter中编写了一个带有几个按钮的GUI。每个按钮都链接到一个类,该类实际上运行一个简短的脚本。单击该按钮时,我会初始化一个类log_window,它只是一个Tkinter文本小部件。然后我创建了一个全局变量,将log链接到我刚刚创建的log_window,并且当脚本运行时,我将sys.stdout/stderr传递给log(我专门为此创建了一个写入方法)。一切都是犹太的,除了log_window文本小部件不会用我的管道标准输出更新,直到调用它的类完成。但是,如果我只是在类中print,它将按照调用的顺序打印。

实施例

import Tkinter
from Tkinter import *
import time

class log_window:
    def __init__(self,master):
        self.textframe = Tkinter.Frame(master)
        self.text = Text(self.textframe)
        self.text.pack()
        self.textframe.pack()
    def write(self,text):
        self.text.insert(END,text)

class some_func1: # This effectively waits 5 seconds then prints both lines at once
    def __init__(self,master):
        log.write("some text")
        time.sleep(5)
        log.write("some text")

class some_func2: # This prints the first object, waits 5 seconds, then prints the second
    def __init__(self,master):
        print "some text"
        time.sleep(5)
        print "some text"

if __name__ == '__main__':
    global log    
    root = Tk()
    log = log_window(root)
    root.after(100,some_func1, root)
    root.after(100,some_func2, root)
    root.mainloop()

对不起,如果我的例子有点闷,但我认为这是重点。我做的管道是通过Popen和一些系统调用,但它们不是问题的一部分,所以我只强调了我认为是问题的LCD。

3 个答案:

答案 0 :(得分:8)

我不知道Tkinter的并发性的细节,但摆弄却揭示了如果你把

master.update_idletasks()

每次调用log.write后,都会根据提示进行更新。您可以为日志提供.flush()方法(如文件句柄),或者您可以在写完后log.write调用它。

答案 1 :(得分:2)

当您致电sleep时,会导致整个GUI冻结。您必须记住,您的GUI运行一个事件循环,这是一个包装所有代码的无限循环。事件循环负责使小部件在更改时重绘。当绑定被触发时,它会从该循环中调用您的代码,因此只要您的代码正在运行,事件循环就无法循环。

你有几个选择。一种是在向窗口小部件添加文本后调用update_idletasks。这使得事件循环服务“在空闲时”事件 - 当程序没有执行任何其他操作时计划运行的事情。重新绘制屏幕是一个这样的事件,还有其他事件。

另一种选择是在线程或单独的进程中运行您的函数。由于Tkinter不是线程安全的,因此这些其他线程或进程无法直接与GUI通信。他们必须做的是将消息推送到队列,然后您的主(GUI)线程必须轮询队列并关闭消息。将此代码构建到日志类中很容易,并且可以使用事件循环来轮询队列 - 只需编写一个方法将消息从队列中拉出并将它们插入到小部件中,调用本身使用{{1几百毫秒后。

答案 2 :(得分:0)

您必须通过在self.text.update()之后添加self.text.insert(END,text)来更新窗口小部件内容