问题:我有一个 PySide 应用程序,它已经使用日志记录控制台输出,但其日志记录应该以 LogRecords 的方式进行扩展也会像QTextBrowser
一样在小部件中显示立即。我知道这通常是通过一个工作线程来完成的,该工作线程指示main / gui线程中的一个插槽,但是由于代码库相当大,并且日志记录可能用于一些阻塞核心如果没有更大的重构,无论如何都可以实现GUI中的即时反馈,那将是很好的。
示例:以下是一些示例代码示例。它显示:
logger
,有两个处理程序:
StreamHandler
登录到控制台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
时,{em> PySide 事件循环会发生QSignalHandler
日志记录再次处理事件,这发生在for
循环之后。
QSignalHandler
实现立即日志记录?QtGui.qApp.processEvents()
发出记录信号后调用QSignalHandler
吗? (当取消注释时,直接登录到GUI)。Qt.DirectConnection: The slot is invoked immediately, when the signal is emitted.
我会认为QSignalHandler
应该像StreamHandler
一样立即更新,不应该吗?答案 0 :(得分:3)
是否有推荐的方法,即可在不调用核心操作的工作线程的情况下立即从QSignalHandler进行日志记录?
我不知道触发重绘日志小部件的任何其他方式,而不是处理事件。
请注意,在日志窗口小部件上调用repaint()
会产生误导并且没有所需的效果,它只会强制调用日志窗口小部件的paintEvent()
方法。 repaint()
没有做关键的事情,比如将窗口表面复制到窗口系统。
在QSignalHandler发出记录信号后,是否安全/建议只调用QtGui.qApp.processEvents()? (取消注释时,直接登录到GUI)。
建议使用单独的线程或异步操作。如果您不能像在您的情况下那样调用processEvents()
,则建议使用QProgressDialog::setValue()
。甚至Qt也会在processEvents()
内将其用于相同目的。
通常,手动处理事件可能很危险,应谨慎处理。调用QTextBrowser::append()
后,完整的应用程序状态可能会有所不同。例如,日志窗口小部件可能不再存在,因为用户关闭了窗口!在您的示例代码中没有问题,因为信号/插槽连接将自动断开连接,但想象一下,如果您因为关闭而被删除后尝试访问日志窗口小部件 - 您可能会遇到崩溃。所以要小心。
在阅读信号连接类型的文档时,它说Qt.DirectConnection:当发出信号时,立即调用插槽。我会认为QSignalHandler应该像StreamHandler一样立即更新,不应该吗?
您的案例QTextBrowser::append()
, 中的广告位立即被调用。但是,processEvents()
不会立即重新绘制。相反,它会安排重新绘制(通过QWidget::update()),并在Qt处理事件时进行实际重新绘制。这是当您返回事件循环或手动调用DirectConnection
时。
因此,在发出信号时确实会立即调用插槽,至少在使用默认 $send_mail = mail($to_email, $subject, $message_body, $headers);
if(!$send_mail)
{
//If mail couldn't be sent output error. Check your PHP email configuration (if it ever happens)
$output = json_encode(array('type'=>'message', 'text' => 'Hi '.$user_name .' Achtung, es gab einen Fehler bei der Übertragung !'));
die($output);
}else{
$output = json_encode(array('type'=>'message', 'text' => 'Hi '.$user_name .' Vielen Dank für Ihre Anfrage. Wir prüfen diese umgehend und geben Ihnen schnellstmöglich eine Antwort'));
die($output);
}
}
时。然而重新粉刷不会立即发生。