web-dev-qa-db-ja.com

Pyqtでログを表示する最良の方法?

私は現在、qtデザイナーを使用してGUIに取り組んでいます。ロガーウィンドウのように機能するGUIで文字列を印刷する方法を知りたいのですが。私はpyqt5を使用しています。

15
Carlo Angelo

Python loggingモジュールを使用すると、ログメッセージをQPlainTextEditインスタンスに渡すカスタムロギングハンドラーを簡単に作成できます(Christopher )。

これを行うには、まず_logging.Handler_をサブクラス化します。この___init___では、ログを含むQPlainTextEditを作成します。ここで重要なのは、ハンドルがemit()関数を介してメッセージを受信することです。したがって、この関数をオーバーロードして、メッセージテキストをQPlainTextEditに渡します。

_import logging

class QPlainTextEditLogger(logging.Handler):
    def __init__(self, parent):
        super(Logger, self).__init__()

        self.widget = QPlainTextEdit(parent)
        self.widget.setReadOnly(True)

    def emit(self, record):
        msg = self.format(record)
        self.widget.textCursor().appendPlainText(msg)

    def write(self, m):
        pass
_

このクラスからオブジェクトを作成し、QPlainTextEditの親を渡します(メインウィンドウやレイアウトなど)。次に、このハンドラーを現在のロガーに追加できます。

_# Set up logging to use your widget as a handler
log_handler = QPlainTextEditLogger(<parent widget>)
logging.getLogger().addHandler(log_handler)
_
13
mfitzp

PyQt5の Todd Vanyo の例から適応:

import sys
from PyQt5 import QtWidgets
import logging

# Uncomment below for terminal log messages
# logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s')

class QTextEditLogger(logging.Handler):
    def __init__(self, parent):
        super().__init__()
        self.widget = QtWidgets.QPlainTextEdit(parent)
        self.widget.setReadOnly(True)

    def emit(self, record):
        msg = self.format(record)
        self.widget.appendPlainText(msg)


class MyDialog(QtWidgets.QDialog, QtWidgets.QPlainTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)

        logTextBox = QTextEditLogger(self)
        # You can format what is printed to text box
        logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        logging.getLogger().addHandler(logTextBox)
        # You can control the logging level
        logging.getLogger().setLevel(logging.DEBUG)

        self._button = QtWidgets.QPushButton(self)
        self._button.setText('Test Me')

        layout = QtWidgets.QVBoxLayout()
        # Add the new logging box widget to the layout
        layout.addWidget(logTextBox.widget)
        layout.addWidget(self._button)
        self.setLayout(layout)

        # Connect signal to slot
        self._button.clicked.connect(self.test)

    def test(self):
        logging.debug('damn, a bug')
        logging.info('something to remember')
        logging.warning('that\'s not right')
        logging.error('foobar')

app = QtWidgets.QApplication(sys.argv)
dlg = MyDialog()
dlg.show()
dlg.raise_()
sys.exit(app.exec_())
11
Alex

mfitzpの答え に基づく完全な動作例を次に示します。

import sys
from PyQt4 import QtCore, QtGui
import logging

# Uncomment below for terminal log messages
# logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(name)s - %(levelname)s - %(message)s')    

class QPlainTextEditLogger(logging.Handler):
    def __init__(self, parent):
        super().__init__()
        self.widget = QtGui.QPlainTextEdit(parent)
        self.widget.setReadOnly(True)    

    def emit(self, record):
        msg = self.format(record)
        self.widget.appendPlainText(msg)    


class MyDialog(QtGui.QDialog, QPlainTextEditLogger):
    def __init__(self, parent=None):
        super().__init__(parent)    

        logTextBox = QPlainTextEditLogger(self)
        # You can format what is printed to text box
        logTextBox.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        logging.getLogger().addHandler(logTextBox)
        # You can control the logging level
        logging.getLogger().setLevel(logging.DEBUG)

        self._button = QtGui.QPushButton(self)
        self._button.setText('Test Me')    

        layout = QtGui.QVBoxLayout()
        # Add the new logging box widget to the layout
        layout.addWidget(logTextBox.widget)
        layout.addWidget(self._button)
        self.setLayout(layout)    

        # Connect signal to slot
        self._button.clicked.connect(self.test)    

    def test(self):
        logging.debug('damn, a bug')
        logging.info('something to remember')
        logging.warning('that\'s not right')
        logging.error('foobar')

if (__name__ == '__main__'):
    app = None
    if (not QtGui.QApplication.instance()):
        app = QtGui.QApplication([])
    dlg = MyDialog()
    dlg.show()
    dlg.raise_()
    if (app):
        app.exec_()
10
Todd Vanyo

アレックスの答えはシングルスレッドのシナリオでは問題ないはずですが、別のスレッド(QThread)にログインしている場合は、次の警告が表示されることがあります。

_QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
_

これは、Qtシグナル/スロットメカニズムを使用せずに、メインスレッド以外のスレッドからGUI(self.widget.appendPlainText(msg))を変更しているためです。

これが私の解決策です:

_# my_logger.py

import logging
from PyQt5.QtCore import pyqtSignal, QObject

class Handler(QObject, logging.Handler):
    new_record = pyqtSignal(object)

    def __init__(self, parent):
        super().__init__(parent)
        super(logging.Handler).__init__()
        formatter = Formatter('%(asctime)s|%(levelname)s|%(message)s|', '%d/%m/%Y %H:%M:%S')
        self.setFormatter(formatter)

    def emit(self, record):
        msg = self.format(record)
        self.new_record.emit(msg) # <---- emit signal here

class Formatter(logging.Formatter):
    def formatException(self, ei):
        result = super(Formatter, self).formatException(ei)
        return result

    def format(self, record):
        s = super(Formatter, self).format(record)
        if record.exc_text:
            s = s.replace('\n', '')
        return s

_
_   # gui.py

   ... # GUI code
   ...
   def setup_logger(self)
        handler = Handler(self)
        log_text_box = QPlainTextEdit(self)
        self.main_layout.addWidget(log_text_box)
        logging.getLogger().addHandler(handler)
        logging.getLogger().setLevel(logging.INFO)
        handler.new_record.connect(log_text_box.appendPlainText) # <---- connect QPlainTextEdit.appendPlainText slot
   ...
_
1
Chweng Mega

読み取り専用に設定された QPlainTextEdit ウィジェットを使用したいようです。

背景色を灰色に変更して、編集できないことをユーザーに示すことを検討してください。スクロール可能にするか、テキストを選択可能にするかは、あなた次第です。

この答え QPlainTextEditをサブクラス化して、出力をスクロールしたり、ファイルに保存したりできます。