我正在绑定,因为这是在分类机器上写的,我无法复制+粘贴在这里。作为一个新手,我的方法可能是非正统的。
我在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。
答案 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)
来更新窗口小部件内容