将标准输出重定向到 tkinter 文本小部件

redirect stdout to tkinter text widget

我正在尝试将函数的标准输出重定向到 tkinter 文本小部件。 我 运行 遇到的问题是它将每一行写入一个新的 window 而不是将所有内容都列在一个中。 该函数扫描目录并列出所有 0k 文件。如果没有文件是 0k,它会打印出来。所以,问题是如果目录中有 30 个 0k 文件,它将打开 30 windows,每个文件只有一行。 现在,我知道问题出在哪里了。如果您查看我的函数代码 Zerok(),我告诉它:

if os.stat(filename).st_size==0:  
       redirector(filename)

我知道每次 os.stat 看到一个 0k 的文件,然后将其发送到重定向器,这就是为什么每个文件都有一个新的 window。 我只是不知道如何解决它。 完整代码如下。 感谢您的帮助。

import Tkinter
from Tkinter import *
import tkFileDialog

class IORedirector(object):
    '''A general class for redirecting I/O to this Text widget.'''
    def __init__(self,text_area):
        self.text_area = text_area

class StdoutRedirector(IORedirector):
    '''A class for redirecting stdout to this Text widget.'''
    def write(self,str):
        self.text_area.write(str,False)

def redirector(inputStr):
    import sys
    root = Tk()
    sys.stdout = StdoutRedirector(root)
    T = Text(root)
    T.pack()
    T.insert(END, inputStr)

####This Function checks a User defined directory for 0k files
def Zerok():
    import os
    sys.stdout.write = redirector #whenever sys.stdout.write is called, redirector is called.
    PATH = tkFileDialog.askdirectory(initialdir="/",title='Please select a directory')  
    for root,dirs,files in os.walk(PATH):  
     for name in files:  
      filename=os.path.join(root,name)  
      if os.stat(filename).st_size==0:  
       redirector(filename)
      else:
          redirector("There are no empty files in that Directory")
          break

#############################Main GUI Window###########################
win = Tk()
f = Frame(win)
b1 = Button(f,text="List Size")
b2 = Button(f,text="ZeroK")
b3 = Button(f,text="Rename")
b4 = Button(f,text="ListGen")
b5 = Button(f,text="ListDir")
b1.pack()
b2.pack()
b3.pack()
b4.pack()
b5.pack()
l = Label(win, text="Select an Option")
l.pack()
f.pack()
b2.configure(command=Zerok)
win.mainloop()

解决方法很简单:不要创建多个重定向器。重定向器的全部意义在于您创建它一次,然后正常的打印语句将显示在 window.

您需要对 redirector 函数进行一些小的更改。首先,它不应该调用 Tk;相反,它应该创建一个 Toplevel 的实例,因为 tkinter 程序必须只有一个根 window。其次,您必须将文本小部件传递给 IORedirector,因为它需要知道要写入的确切小部件。

def redirector(inputStr=""):
    import sys
    root = Toplevel()
    T = Text(root)
    sys.stdout = StdoutRedirector(T)
    T.pack()
    T.insert(END, inputStr)

接下来,您应该只调用一次此函数。从那时起,要让数据出现在 window 中,您将使用普通的 print 语句。

您可以在主代码块中创建它:

win = Tk()
...
r = redirector()
win.mainloop()

接下来,您需要修改 write 函数,因为它必须写入文本小部件:

class StdoutRedirector(IORedirector):
    '''A class for redirecting stdout to this Text widget.'''
    def write(self,str):
        self.text_area.insert("end", str)

最后,将您的 Zerok 函数更改为使用打印语句:

def Zerok(): ... 如果 os.stat(文件名).st_size==0:
打印(文件名) 别的: 打印("There are no empty files in that Directory") 休息

上面的解决方案很完整;我基本上可以将其复制并粘贴到我的代码中,只需进行一点小修改。我不完全确定 为什么 ,但是 StdoutRedirector 需要一个刷新方法。

我猜这是因为 sys.stdout 在退出时调用了 flush() 方法,但我还没有深入研究文档以真正理解这意味着什么。

运行上述代码在Jupyter环境下导致代码无限挂起,直到内核重启。控制台踢出以下错误:

sys.stdout.flush()
AttributeError: 'StdoutRedirector' object has no attribute 'flush'
ERROR:tornado.general:Uncaught exception, closing connection.

简单的解决方案是通过添加刷新方法对 StdoutRedirector class 做一个小改动。

class StdoutRedirector(IORedirector):
    '''A class for redirecting stdout to this Text widget.'''

    def write(self,str):
        self.text_area.insert("end", str)
    def flush(self):
        pass

感谢在我之前出现的巨人,并提供了非常清晰的解释。