2013-02-14 139 views
4

花了一些時間將stdout和日誌輸出重定向到tkinter文本部件,我決定需要一些幫助。我的代碼如下:將Python記錄器的輸出重定向到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

這是一個完全修改後的答案,可以做你想做的。我試圖指出你問題中哪些代碼行發生了變化,以及添加了哪些新行。

默認情況下,內置的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() 
+1

感謝您的回覆,但我已經有方法在我的代碼中重定向標準輸出。問題在於,即使您專門將stdout作爲處理程序添加到記錄器,Python記錄器也不會寫入stdout。當您重定向標準輸出時,至少來自記錄器的消息不包括在內。 – user2073502 2013-02-15 08:57:11

4

只是爲了確保我的理解正確:

您想要打印的日誌消息都以您的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) 

現在讓我們來覆蓋日誌處理程序重定向到參數控制檯並仍然自動打印到STDout:

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 

希望它有幫助。

+0

我建議在__init__函數中刪除對'pack'的調用,否則只能在使用包的框架中使用這個類。作爲一般規則,如果創建小部件的函數也負責其佈局,而不是讓小部件自行佈局,那麼您的代碼將更易於使用。 – 2013-08-12 19:19:26

+0

當我在一個LabelFrame中使用多個小部件來創建'MegaWidget'時,我通常會在init中打包子小部件,因此用戶只需將LabelFrame放置在他想要的位置,並且其中的所有內容都已正確放置。我可以在這個例子中看到它是如何使用的,但是爲了簡單起見,我除去了文本部件以外的所有東西。 – Asics 2013-08-12 20:48:57

+0

哦,我的壞!我沒有看到init中的框架。是的,內部小工具應該在內部進行管理。對困惑感到抱歉。 – 2013-08-12 23:29:30