Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将Python记录器的输出重定向到tkinter小部件_Python_Tkinter - Fatal编程技术网

将Python记录器的输出重定向到tkinter小部件

将Python记录器的输出重定向到tkinter小部件,python,tkinter,Python,Tkinter,花了一些时间重定向标准输出并将输出记录到tkinter文本小部件,我决定需要一些帮助。我的代码如下: #!/usr/bin/env python from Tkinter import * import logging from threading import Thread class IODirector(object): def __init__(self,text_area): self.text_area = text_area class StdoutDi

花了一些时间重定向标准输出并将输出记录到tkinter文本小部件,我决定需要一些帮助。我的代码如下:

#!/usr/bin/env python
from Tkinter import *
import logging
from threading import Thread

class IODirector(object):
    def __init__(self,text_area):
        self.text_area = text_area

class StdoutDirector(IODirector):
    def write(self,str):
        self.text_area.insert(END,str)
    def flush(self):
        pass

class App(Frame):

    def __init__(self, master):
        self.master = master
        Frame.__init__(self,master,relief=SUNKEN,bd=2)
        self.start()

    def start(self):
        self.master.title("Test")
        self.submit = Button(self.master, text='Run', command=self.do_run, fg="red")
        self.submit.grid(row=1, column=2)
        self.text_area = Text(self.master,height=2.5,width=30,bg='light cyan')
        self.text_area.grid(row=1,column=1)

    def do_run(self):
        t = Thread(target=print_stuff)
        sys.stdout = StdoutDirector(self.text_area)
        t.start()

def print_stuff():
    logger = logging.getLogger('print_stuff')
    logger.info('This will not show')
    print 'This will show'
    print_some_other_stuff()

def print_some_other_stuff():
    logger = logging.getLogger('print_some_other_stuff')
    logger.info('This will also not show')
    print 'This will also show'

def main():    
    logger = logging.getLogger('main')
    root = Tk()
    app = App(root)
    root.mainloop() 

if __name__=='__main__':
    main()

我知道可以基于文本小部件定义一个新的日志处理程序,但我无法让它工作。函数“print_stuff”实际上只是许多不同函数的包装,所有函数都有自己的记录器设置。我需要帮助定义一个新的“全局”日志处理程序,以便可以从每个拥有自己日志的函数中实例化它。非常感谢您的帮助。

这里有一个完全修改过的答案,它符合您的要求。我试图指出您问题中的哪些代码行被更改了,以及在哪里添加了新行

默认情况下,内置的
logger.StreamHandler
handler类将消息输出到
sys.stderr
,因此要使消息也重定向到
sys.stdout
,文本小部件需要构造一个新的记录器,并设置自定义控制台处理程序来执行此操作。由于您希望此设置应用于模块中的所有记录器,因此需要将此设置应用于所有其他命名记录器将从中继承其设置的无名“根”记录器

from Tkinter import *
import logging
from threading import Thread

class IODirector(object):
    def __init__(self, text_area):
        self.text_area = text_area

class StdoutDirector(IODirector):
    def write(self, msg):
        self.text_area.insert(END, msg)
    def flush(self):
        pass

class App(Frame):
    def __init__(self, master):
        self.master = master
        Frame.__init__(self, master, relief=SUNKEN, bd=2)
        self.start()

    def start(self):
        self.master.title("Test")
        self.submit = Button(self.master, text='Run', command=self.do_run, fg="red")
        self.submit.grid(row=1, column=2)
        self.text_area = Text(self.master, height=2.5, width=30, bg='light cyan')
        self.text_area.grid(row=1, column=1)

    def do_run(self):
        t = Thread(target=print_stuff)
        sys.stdout = StdoutDirector(self.text_area)
        # configure the nameless "root" logger to also write           # added
        # to the redirected sys.stdout                                 # added
        logger = logging.getLogger()                                   # added
        console = logging.StreamHandler(stream=sys.stdout)             # added
        logger.addHandler(console)                                     # added
        t.start()

def print_stuff():
    logger = logging.getLogger('print_stuff') # will inherit "root" logger settings
    logger.info('This will now show')                                  # changed
    print 'This will show'
    print_some_other_stuff()

def print_some_other_stuff():
    logger = logging.getLogger('print_some_other_stuff') # will inherit "root" logger settings
    logger.info('This will also now show')                             # changed
    print 'This will also show'

def main():
    logging.basicConfig(level=logging.INFO) # enable logging           # added
    root = Tk()
    app = App(root)
    root.mainloop()

if __name__=='__main__':
    main()

为了确保我正确理解:

您希望将日志消息打印到STDout和Tkinter文本小部件,但日志不会在标准控制台中打印

如果这真的是你的问题,这里有一些方法

首先,让我们在Tkinter中创建一个非常简单的控制台,它可以是任何文本小部件,但为了完整性,我将其包括在内:

class LogDisplay(tk.LabelFrame):
"""A simple 'console' to place at the bottom of a Tkinter window """
    def __init__(self, root, **options):
        tk.LabelFrame.__init__(self, root, **options);

        "Console Text space"
        self.console = tk.Text(self, height=10)
        self.console.pack(fill=tk.BOTH)
现在,让我们重写日志处理程序以重定向到参数中的控制台,并仍然自动打印到标准输出:

class LoggingToGUI(logging.Handler):
""" Used to redirect logging output to the widget passed in parameters """
    def __init__(self, console):
        logging.Handler.__init__(self)

        self.console = console #Any text widget, you can use the class above or not

    def emit(self, message): # Overwrites the default handler's emit method
        formattedMessage = self.format(message)  #You can change the format here

        # Disabling states so no user can write in it
        self.console.configure(state=tk.NORMAL)
        self.console.insert(tk.END, formattedMessage) #Inserting the logger message in the widget
        self.console.configure(state=tk.DISABLED)
        self.console.see(tk.END)
        print(message) #You can just print to STDout in your overriden emit no need for black magic

希望能有所帮助。

谢谢您的回复,但我已经有办法在代码中重定向stdout。问题是,即使您专门将stdout添加为记录器的处理程序,Python记录器似乎也不会写入stdout。重定向stdout时,至少不包括来自记录器的消息。我建议删除init函数中对
pack
的调用,否则只能在使用pack的帧中使用此类。一般来说,如果创建小部件的函数也负责其布局,那么代码将更易于使用,当我在一个LabelFrame中使用几个小部件来创建一个“MegaWidget”时,我通常将子小部件打包在init中,这样用户只需将LabelFrame放置在他想要的地方,里面的所有东西都将被正确放置。在这个例子中,我可以看到它是如何使用的,因为为了简单起见,我删除了除了文本小部件以外的所有东西。哦,我的糟糕!我在初始化中看不到帧。是的,内部小部件应该在内部管理。很抱歉给你带来了困惑。