【问题标题】:Issue with Redirecting stdout to Tkinter text widget with threads使用线程将标准输出重定向到 Tkinter 文本小部件的问题
【发布时间】:2013-12-16 16:26:32
【问题描述】:

只有当您在重定向sys.stdout 后打印某些内容时,我的打印无限数字集的程序才能按预期工作,否则程序会冻结,这是为什么呢?这是一个错误吗?

代码:

import Tkinter as Tk
import sys
import threading

def func():
    i = 0
    while True:
        i +=1
        print i

class Std_redirector(object):
    def __init__(self,widget):
        self.widget = widget

    def write(self,string):
        self.widget.see(Tk.END)
        self.widget.insert("end",string)


root = Tk.Tk()
text = Tk.Text(root)
text.pack()

sys.stdout = Std_redirector(text) #Redirect stdout to Tkinter text widget

#print 'hey' #If you uncomment this line, the program works!

thread1 = threading.Thread(target=func)
thread1.start() #Starts printing

root.mainloop()

如果您未注释此行,则此脚本有效:print 'hey'

顺便说一句,我的操作系统是 windows 7

【问题讨论】:

  • 它适用于我 - 直接在 Python 中。在类似于IDLEDreamPie python shell 中,我收到错误'Std_redirector' object has no attribute 'flush',但它仍然有效。 (Linux Mint,Python 2.7.5)
  • @furas 在此处的 windows 7 中的 python 2.7 它没有...
  • @furas 哦,你真的评论了print 'hey' 行...顺便说一句,只需运行更新的代码
  • @K DawG 新版本(与旧版本一样)可以使用和不使用print 'hey',但我使用 Linux。只能是 Windows 问题。

标签: python multithreading python-2.7 tkinter stdout


【解决方案1】:

我对这个想法进行了修改,并找到了两种让它发挥作用的方法。

1) 即使它不是线程安全的,您的方法也有效。唯一的问题似乎是在打印到小部件开始之前需要初始化应用程序。如果您想“立即”启动第二个线程,而不是从某个回调开始,这对我有用:

root.after(100, thread1.start)

2) 第二种更简洁的方法基于 @falsetru 链接的示例。但是,它要求您以合理的速度打印到标准输出,这样更新就不会阻塞。

from Tkinter import *
import threading
import Queue # This is thread safe
import time

class Std_redirector():
    def __init__(self, widget):
        self.widget = widget

    def write(self,string):
        self.widget.write(string)

class ThreadSafeText(Text):
    def __init__(self, master, **options):
        Text.__init__(self, master, **options)
        self.queue = Queue.Queue()
        self.update_me()

    def write(self, line):
        self.queue.put(line)

    def update_me(self):
        while not self.queue.empty():
            line = self.queue.get_nowait()
            self.insert(END, line)
            self.see(END)
            self.update_idletasks()
        self.after(10, self.update_me)

def func():
    i = 0
    while True:
        i += 1
        print i
        time.sleep(0.01)

root = Tk()
text = ThreadSafeText(root)
text.pack()
sys.stdout = Std_redirector(text)

thread1 = threading.Thread(target=func)
thread1.start()

root.mainloop()

根据我在其他 GUI 工具包中的经验,我想使用 root.after_idle(),但它并没有像我预期的那样工作。

【讨论】:

  • 感谢您的帮助,顺便说一句,这是一个错误吗?
  • 我不知道。刚从 Tk 开始。但是使用其他 GUI 库的经验一直是,您不应该在主 GUI 线程之外更新您的 GUI。通常使用事件将调用传播到主 GUI 线程,但这对我在 Tkinter 中也不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-13
  • 2013-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多