如何将Python的日志输出重定向到Kivy标签?

3

正如标题所述,我需要以最简单的方式将日志模块输出重定向到Kivy标签。我在网上搜索了解决方案,我认为最好的方法是以某种方式覆盖 StreamHandler MemoryHandler (但我不知道如何做到这一点)。

有人可以帮我实现这个吗?

我使用Python 2.7

谢谢

2个回答

9

我经常为这种情况自己创建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()

请注意这里的简单线程以测试日志记录。

好的解决方案。让我对此提出两个问题:使用StreamHandler而不是基本Handler不是更好吗?你真的需要同时使用schedule函数和线程实例吗?据我所知(如果我没有记错),Kivy Clock类是使用线程实现的。 - user2073384
@Tungsteno 我不确定它是否更好,只是不同而已。Kivy时钟并没有使用线程实现,它只是在Kivy主GUI线程中安排一个事件。调用除Clock以外的其他Kivy方法被认为是不安全的。 - Yoav Glazner
1
优秀的标准答案! - jlandercy

0

你可以将日志写入文件,并在一个独立的线程中使用应用程序读取它。如果不使用独立线程,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()

2
谢谢您的回答,但我认为这个解决方案不够优雅且对IO(硬盘)要求较高。 - user2073384
实际上这并不是一个很糟糕的解决方案。当你准备阅读日志时,它并不会占用太多资源,而且通常你也需要保存它们。这正是我所寻找的。 - Vladimir.Shramov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接