将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 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”函数其实只是一个包装器,里面调用了很多不同的函数,每个函数都有自己的日志记录器。我需要帮助来定义一个新的“全局”日志处理器,这样每个有自己日志记录器的函数都可以使用它。非常感谢任何帮助。
2 个回答
3
这是一个完全修订过的答案,可以满足你的需求。我尽量标明了你问题中的代码哪些行被修改了,哪些行是新加的。
默认情况下,内置的logger.StreamHandler
类会把信息输出到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()
5
我想确认一下我理解得对不对:
你想把日志信息同时打印到标准输出(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
希望这能帮到你。