如何将 Python 的日志输出重定向到 Kivy 标签?
How to redirect Python's logging output to Kivy label?
如标题中所写,我需要以尽可能简单的方式将日志记录模块输出重定向到 Kivy 标签。我在网上搜索了一个解决方案,我认为最好的方法是以某种方式覆盖 StreamHandler 或 MemoryHandler(但我不我不知道该怎么做)。
有人可以帮我实现这个吗?
我用python2.7
谢谢
您可以将日志写入文件,然后在单独的线程中使用您的应用读取它。如果没有单独的线程,GUI 将被冻结。
test.kv:
#:kivy 1.9.1
GridLayout:
cols: 1
LogButton:
text: 'log me'
on_press: self.log_me()
ScrollView:
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
LogLabel:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
halign: 'center'
main.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
import logging
import threading
import time
class LogButton(Button):
def __init__(self, **kwargs):
super(LogButton, self).__init__(**kwargs)
self.set_logger()
def set_logger(self):
self.logger = logging.getLogger('example')
hdlr = logging.FileHandler('example.log')
formatter = logging.Formatter(
'%(asctime)s %(levelname)s %(message)s'
)
hdlr.setFormatter(formatter)
self.logger.addHandler(hdlr)
self.logger.setLevel(logging.DEBUG)
def log_me(self, *x):
self.logger.debug('logger text')
class LogLabel(Label):
def __init__(self, **kwargs):
super(LogLabel, self).__init__(**kwargs)
thread = threading.Thread(target=self.read_log)
thread.daemon = True # kill thread on app close
thread.start()
def read_log(self):
with open('example.log', 'r') as file:
while 1:
where = file.tell()
line = file.readline()
if not line:
time.sleep(0.1)
file.seek(where)
else:
# newest logs on top
self.text = '%s%s' % (line, self.text)
class Test(App):
def build(self):
return self.root
Test().run()
对于这样的情况,我总是自己制作 logging.Handler。
这是一个有效的示例:
main.py:
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
import logging
import thread
import time
def my_thread(log):
for i in range(2**20):
time.sleep(1)
log.info("WOO %s", i)
class MyLabelHandler(logging.Handler):
def __init__(self, label, level=logging.NOTSET):
logging.Handler.__init__(self, level=level)
self.label = label
def emit(self, record):
"using the Clock module for thread safety with kivy's main loop"
def f(dt=None):
self.label.text = self.format(record) #"use += to append..."
Clock.schedule_once(f)
class MyApp(App):
def build(self):
label = Label(text="showing the log here")
log = logging.getLogger("my.logger")
log.level = logging.DEBUG
log.addHandler(MyLabelHandler(label, logging.DEBUG))
thread.start_new(my_thread, (log,))
return label
if __name__ == '__main__':
MyApp().run()
注意此处用于测试日志记录的简单线程。
如标题中所写,我需要以尽可能简单的方式将日志记录模块输出重定向到 Kivy 标签。我在网上搜索了一个解决方案,我认为最好的方法是以某种方式覆盖 StreamHandler 或 MemoryHandler(但我不我不知道该怎么做)。
有人可以帮我实现这个吗?
我用python2.7
谢谢
您可以将日志写入文件,然后在单独的线程中使用您的应用读取它。如果没有单独的线程,GUI 将被冻结。
test.kv:
#:kivy 1.9.1
GridLayout:
cols: 1
LogButton:
text: 'log me'
on_press: self.log_me()
ScrollView:
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
LogLabel:
size_hint_y: None
height: self.texture_size[1]
text_size: self.width, None
halign: 'center'
main.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
import logging
import threading
import time
class LogButton(Button):
def __init__(self, **kwargs):
super(LogButton, self).__init__(**kwargs)
self.set_logger()
def set_logger(self):
self.logger = logging.getLogger('example')
hdlr = logging.FileHandler('example.log')
formatter = logging.Formatter(
'%(asctime)s %(levelname)s %(message)s'
)
hdlr.setFormatter(formatter)
self.logger.addHandler(hdlr)
self.logger.setLevel(logging.DEBUG)
def log_me(self, *x):
self.logger.debug('logger text')
class LogLabel(Label):
def __init__(self, **kwargs):
super(LogLabel, self).__init__(**kwargs)
thread = threading.Thread(target=self.read_log)
thread.daemon = True # kill thread on app close
thread.start()
def read_log(self):
with open('example.log', 'r') as file:
while 1:
where = file.tell()
line = file.readline()
if not line:
time.sleep(0.1)
file.seek(where)
else:
# newest logs on top
self.text = '%s%s' % (line, self.text)
class Test(App):
def build(self):
return self.root
Test().run()
对于这样的情况,我总是自己制作 logging.Handler。
这是一个有效的示例:
main.py:
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
import logging
import thread
import time
def my_thread(log):
for i in range(2**20):
time.sleep(1)
log.info("WOO %s", i)
class MyLabelHandler(logging.Handler):
def __init__(self, label, level=logging.NOTSET):
logging.Handler.__init__(self, level=level)
self.label = label
def emit(self, record):
"using the Clock module for thread safety with kivy's main loop"
def f(dt=None):
self.label.text = self.format(record) #"use += to append..."
Clock.schedule_once(f)
class MyApp(App):
def build(self):
label = Label(text="showing the log here")
log = logging.getLogger("my.logger")
log.level = logging.DEBUG
log.addHandler(MyLabelHandler(label, logging.DEBUG))
thread.start_new(my_thread, (log,))
return label
if __name__ == '__main__':
MyApp().run()
注意此处用于测试日志记录的简单线程。