Лучший способ отображать журналы в pyqt?

В настоящее время я работаю над графическим интерфейсом, используя дизайнер qt. Мне интересно, как мне печатать строки в графическом интерфейсе, который действует как окно регистратора. Я использую pyqt5.


person Carlo Angelo    schedule 22.02.2015    source источник
comment
Пользователи Bing должны перейти здесь: P   -  person Carel    schedule 02.05.2016


Ответы (6)


Адаптировано из примера Todd Vanyo для PyQt5:

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_())
person Alex    schedule 01.08.2018
comment
как использовать в другом случае, у меня есть *.ui с QMainWindow, QTabWidget с вкладкой (имя tab_log), все они определены в файле пользовательского интерфейса. Как добавить QTextEditLogger на вкладку в QTabWidget??? - person emcek; 17.01.2019
comment
Это не потокобезопасно. appendPlainText должен быть подключен к сигналу, а не вызывать его. - person tobilocker; 22.01.2020
comment
Я получаю следующее сообщение об ошибке: AttributeError: module 'logging' has no attribute 'Handler' - person the_economist; 05.11.2020
comment
ВАУ, для меня это была простая копия и вставка. Алекс, ты бог. - person Kris Kizlyk; 15.02.2021
comment
Выдает ошибку Make sure 'QTextCursor' is registered using qRegisterMetaType() при регистрации через обратные вызовы. Поточно-безопасная версия работала у меня довольно хорошо. stackoverflow.com/a/60528393/3598205 - person sa_penguin; 20.07.2021

Если вы используете модуль Python logging, вы можете легко создать собственный обработчик ведения журнала, который передает сообщения журнала в экземпляр QPlainTextEdit (как описано Кристофером).

Для этого вы сначала подкласс logging.Handler. В этом __init__ мы создаем QPlainTextEdit, который будет содержать журналы. Ключевым моментом здесь является то, что дескриптор будет получать сообщения через функцию emit(). Поэтому мы перегружаем эту функцию и передаем текст сообщения в файл QPlainTextEdit.

import logging

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

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

    def emit(self, record):
        msg = self.format(record)
        self.widget.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)
person mfitzp    schedule 01.03.2015

Вот полный рабочий пример, основанный на ответе 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_()
person Todd Vanyo    schedule 24.02.2016
comment
Это может быть глупый вопрос, но какова цель наследования от QPlainTextEditLogger в MyDialog? Я пытаюсь преобразовать этот пример в PyQt5 и не смог заставить его работать, не удалив это второе наследование. Вроде и без него нормально работает. - person Filip S.; 26.01.2018

Поточно-безопасная версия

class QTextEditLogger(logging.Handler, QtCore.QObject):
    appendPlainText = QtCore.pyqtSignal(str)

def __init__(self, parent):
    super().__init__()
    QtCore.QObject.__init__(self)
    self.widget = QtWidgets.QPlainTextEdit(parent)
    self.widget.setReadOnly(True)
    self.appendPlainText.connect(self.widget.appendPlainText)

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

Применение

    logTextBox = QTextEditLogger(self)

    # log to text box
    logTextBox.setFormatter(
        logging.Formatter(
            '%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s'))
    logging.getLogger().addHandler(logTextBox)
    logging.getLogger().setLevel(logging.DEBUG)

    # log to file
    fh = logging.FileHandler('my-log.log')
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(
        logging.Formatter(
            '%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s'))
    logging.getLogger().addHandler(fh)
person tobilocker    schedule 04.03.2020
comment
Спасибо, это было быстро! - person Swedgin; 04.03.2020
comment
Спасибо за ответ. Хотя я не уверен, почему, с PySide2 (5.12.0) двойное наследование у меня не работает. Ошибка в том, что emit() принимает 2 позиционных аргумента, но дано 3. (это функция генерации сигнала, я думаю, это означает, что есть некоторая проблема наследования объекта сигнала) Рабочий пример для меня - создать новый класс QThread/QObject с сигналом и внедрить экземпляр в конструктор обработчика журнала. Что-то вроде этого примера. - person Peter; 24.01.2021

Ответ Алекса должен быть в порядке в сценарии с одним потоком, но если вы входите в другой поток (QThread), вы можете получить следующее предупреждение:

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

Это связано с тем, что вы изменяете GUI (self.widget.appendPlainText(msg)) из потока, отличного от основного потока, без использования механизма Qt Signal/Slot.

Вот мое решение:

# 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
   ...
person Chweng Mega    schedule 08.03.2019
comment
Я просто не мог заставить это работать на PySide2, так как у вас не может быть нескольких наследований с QObject. Чтобы заставить его работать, мне пришлось использовать старый синтаксис сигналов, например это - person Regnareb; 21.01.2020

Похоже, вы захотите использовать виджет QPlainTextEdit, настроенный только для чтения .

Попробуйте изменить цвет фона на серый, чтобы дать пользователю понять, что его нельзя редактировать. Это также зависит от вас, хотите ли вы, чтобы его можно было прокручивать или выделять текст.

Этот ответ может помочь вам начать создавать подклассы QPlainTextEdit для прокрутки с выводом, сохранения в файл, что угодно.

person Christopher Peterson    schedule 22.02.2015