Python立即登录到PySide小部件

2024-10-03 15:30:19 发布

您现在位置:Python中文网/ 问答频道 /正文

问题:我有一个PySide应用程序,它已经将日志记录用于控制台输出,但它的日志记录应该扩展为这样一种方式,即LogRecords也会立即显示在一个小部件中,比如QTextBrowser。我知道这通常是通过一个工作线程来完成的,这个工作线程在主/图形用户界面线程中发出一个插槽信号,但是由于代码库相当大,而且日志记录可能被用于一些阻塞核心操作中,如果可以在gui中获得即时反馈而不需要进行更大的重构,那就太好了。在

示例:下面是一些示例代码以供演示。它显示:

  • 包含两个处理程序的logger
    1. 登录到控制台的StreamHandler
    2. 一个QSignalHandler发出一个信号,其中一条消息连接到一个插槽,该插槽将消息附加到QTextBrowser。在
  • 一种方法long_running_core_operation_that_should_log_immediately_to_ui(),用于模拟阻塞核心操作的日志记录。在
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import logging
import sys

from PySide import QtCore
from PySide import QtGui


class QSignaler(QtCore.QObject):
    log_message = QtCore.Signal(unicode)


class SignalHandler(logging.Handler):
    """Logging handler to emit QtSignal with log record text."""

    def __init__(self, *args, **kwargs):
        super(SignalHandler, self).__init__(*args, **kwargs)
        self.emitter = QSignaler()

    def emit(self, logRecord):
        msg = "{0}".format(logRecord.getMessage())
        self.emitter.log_message.emit(msg)
        # When the line below is enabled, logging is immediate/otherwise events
        # on the queue will be processed when the slot has finished.
        # QtGui.qApp.processEvents()


# configure logging
logging.basicConfig(level=logging.DEBUG)  # adds StreamHandler

signal_handler = SignalHandler()

logger = logging.getLogger()
logger.addHandler(signal_handler)


class TestWidget(QtGui.QWidget):
    def __init__(self, *args, **kwargs):
        super(TestWidget, self).__init__(*args, **kwargs)

        layout = QtGui.QVBoxLayout(self)

        # text_browser
        self.text_browser = QtGui.QTextBrowser()
        layout.addWidget(self.text_browser)

        # btn_start_operation
        self.btn_start_operation = QtGui.QPushButton("Start operation")
        self.btn_start_operation.clicked.connect(
            self.long_running_core_operation_that_should_log_immediately_to_ui)
        layout.addWidget(self.btn_start_operation)

        # btn_clear
        self.btn_clear = QtGui.QPushButton("Clear")
        self.btn_clear.clicked.connect(self.text_browser.clear)
        layout.addWidget(self.btn_clear)

    def long_running_core_operation_that_should_log_immediately_to_ui(self):
        for index in range(10000):
            msg = "{0}".format(index)
            logger.info(msg)


# test
if (__name__ == "__main__"):
    app = QtGui.QApplication(sys.argv)
    test_widget = TestWidget()
    signal_handler.emitter.log_message.connect(test_widget.text_browser.append)
    test_widget.show()
    sys.exit(app.exec_())

问题:StreamHandler记录到stdout时,当PySide事件循环再次处理事件时,QSignalHandler日志记录发生在for循环之后。在

  • 有没有推荐的方法可以在不调用核心操作的工作线程的情况下实现从QSignalHandler的即时日志记录吗?在
  • QSignalHandler发出日志信号后,是否安全/建议只调用QtGui.qApp.processEvents()(未注释时,直接记录到GUI)。在
  • 当读取信号连接类型的documentation时,我会想QSignalHandler应该像StreamHandler那样立即更新,不是吗?在

Tags: textimportselfbrowserlog信号logging记录
1条回答
网友
1楼 · 发布于 2024-10-03 15:30:19

Is there a recommended way, to achieve immediate logging from the QSignalHandler without invoking a worker thread for the core operation?

除了处理事件之外,我不知道有其他方法可以触发日志小部件的重绘。在

请注意,在日志小部件上调用repaint()是误导性的,并且没有预期的效果,它只会强制调用日志小部件的paintEvent()方法。repaint()不做关键的事情,比如将窗口表面复制到窗口系统。在

Is it safe/recommended to just call QtGui.qApp.processEvents() after the QSignalHandler has emitted the logging signal? (When uncommented, logging to the GUI happens directly).

建议使用单独的线程或异步操作。如果您不能调用processEvents(),则建议使用这种方法,例如在您的案例中。。甚至Qt在QProgressDialog::setValue()中也使用它。在

一般来说,手动处理事件是危险的,应该小心处理。调用processEvents()后,完整的应用程序状态可能不同。例如,日志小部件可能不再存在,因为用户关闭了窗口!在您的示例代码中,这是没有问题的,因为信号/插槽连接将自动断开连接,但是想象一下,如果您试图在日志小部件由于关闭而被删除之后访问它,那么您可能会崩溃。所以要小心。在

When reading the documentation for signal connection types, where it says Qt.DirectConnection: The slot is invoked immediately, when the signal is emitted. I would have kind of thought the QSignalHandler should have updated immediately just as the StreamHandler does, shouldn't it?

在您的示例中,QTextBrowser::append()会立即调用。但是,QTextBrowser::append()不会立即重新绘制。相反,它调度一个重绘(通过QWidget::update()),实际的重绘发生在Qt处理事件时。当您返回到事件循环时,或者当您手动调用processEvents()时。 因此,插槽确实在发出信号时立即调用,至少在使用默认的DirectConnection时是这样。然而,重新油漆不会立即发生。在

相关问题 更多 >