Python 在pyqt中显示日志的最佳方法?

Python 在pyqt中显示日志的最佳方法?,python,pyqt,qt-designer,Python,Pyqt,Qt Designer,我目前正在使用qt designer开发GUI。我想知道我应该如何在GUI上打印字符串,就像一个日志窗口一样。我正在使用pyqt5。听起来您可能想使用设置为只读的小部件 考虑将背景颜色更改为灰色,以提示用户该颜色不可编辑。它也取决于你是否希望它是可滚动的或文本可选择的 可以开始对QPlainTextEdit进行子类化,以滚动输出、保存到文件,等等。如果使用Python日志记录模块,可以轻松创建自定义日志处理程序,将日志消息传递到QPlainTextEdit实例(如Christopher所述) 要

我目前正在使用qt designer开发GUI。我想知道我应该如何在GUI上打印字符串,就像一个日志窗口一样。我正在使用pyqt5。

听起来您可能想使用设置为只读的小部件

考虑将背景颜色更改为灰色,以提示用户该颜色不可编辑。它也取决于你是否希望它是可滚动的或文本可选择的


可以开始对QPlainTextEdit进行子类化,以滚动输出、保存到文件,等等。

如果使用Python
日志记录
模块,可以轻松创建自定义日志处理程序,将日志消息传递到
QPlainTextEdit
实例(如Christopher所述)

要做到这一点,首先要创建一个子类
logging.Handler
。在这个
\uuuu init\uuuu
中,我们创建将包含日志的
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)
#设置日志以将小部件用作处理程序
log_handler=QPlainTextEditLogger()
logging.getLogger().addHandler(日志处理程序)

以下是一个完整的工作示例:

改编自的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_())

Alex的回答在单线程场景中应该是可以的,但是如果您正在登录另一个线程(QThread),您可能会收到以下警告:

QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
这是因为您正在从主线程以外的线程修改GUI(
self.widget.appendPlainText(msg)
),而不使用Qt信号/插槽机制

以下是我的解决方案:

#my_logger.py
导入日志记录
从PyQt5.QtCore导入pyqtSignal,QObject
类处理程序(QObject,logging.Handler):
新记录=pyqtSignal(对象)
定义初始化(自身,父级):
super()。\uuuu init\uuuu(父级)
super(logging.Handler)。\uuuu init\uuuuuu()
格式化程序=格式化程序('%(asctime)s |%(levelname)s |%(message)s |',“%d/%m/%Y%H:%m:%s')
self.setFormatter(格式化程序)
def排放(自我,记录):
msg=self.format(记录)
self.new_record.emit(msg)#线程安全版本
用法

Bing用户应该这样做:p这可能是一个愚蠢的问题,但是继承
MyDialog
中的
QPlainTextEditLogger
的目的是什么?我正在尝试将这个示例转换为PyQt5,如果不删除第二个继承,就无法让它工作。在其他情况下如何使用,我有带QMainWindow的*.ui,带tab(name tab_log)的QTabWidget,都是在ui文件中定义的。如何将QTextEditLogger添加到QTabWidget中的选项卡???这不是线程安全的
appendPlainText
应该连接到信号而不是调用它。我收到以下错误消息:
AttributeError:module'logging'没有属性'Handler'
哇,这对我来说是一个简单的复制粘贴。亚历克斯,你真是个神。我只是不能让它在PySide2上运行,因为你不能在QObject上拥有多个继承权。为了让它工作,我不得不使用旧的信号语法,如感谢,这很快!谢谢你的回答。虽然我不知道为什么,但在PySide2(5.12.0)中,双重继承对我来说不起作用。错误是“emit()接受2个位置参数,但给出了3个”。(这是signal emit函数,我认为这意味着signal对象存在一些继承问题)我的一个工作示例是使用该信号创建一个新的QThread/QObject类,并将一个实例注入日志处理程序构造函数。有点像一个例子。
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
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)