日志记录发射函数和QT发射信号之间的名称冲突



每次调用日志处理程序emit函数时,我都会尝试发送一个Qt信号。但我认为MyLogHandler.emit和log.emit函数是冲突的。

from PySide2.QtCore import QObject, Signal
import logging
class MyLogHandler(logging.Handler, QObject):
log = Signal(str)
def emit(self, record):
self.log.emit('send')
if __name__ == "__main__":
logging.getLogger().addHandler(MyLogHandler())
logging.warning('logging test')

错误:

TypeError: emit() takes 2 positional arguments but 3 were given

更新:

我尝试过使用composition(per@eylanesc(,但仍然无法将信号连接到QML文件。我不知道为什么我不能接收QML中的信号。它似乎没有发出任何东西。我做错了什么?

from functools import cached_property
import logging
import sys
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QCoreApplication, QObject, QTimer, Signal, QUrl
class Main(QObject):
log = Signal(str)

class Log(object):
def __init__(self):
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("debug.log", mode='w'),
logging.StreamHandler(),
MyLogHandler()
]
)
class MyLogHandler(logging.Handler):
@cached_property
def main(self):
return Main()
def emit(self, record):
msg = self.format(record)
self.main.log.emit(msg)

if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
main = Main()
Log()
QTimer.singleShot(1000, lambda: logging.warning("logging test"))
engine.rootContext().setContextProperty("main", main)
engine.load(QUrl("Main3.qml"))
app.exec_()

QML:Main3.QML

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 500
height: 500
visible: true
title: qsTr("MIST")
Text {
id: text
anchors.fill: parent
}
Connections {
target: main
function onLog(msg) {
text.text = msg
}
}
}

问题是两个基类都有一个导致冲突的emit()方法。解决方法是不使用继承,而是使用组合:

from functools import cached_property
import logging
from PySide2.QtCore import QCoreApplication, QObject, QTimer, Signal

class Bridge(QObject):
log = Signal(str)

class MyLogHandler(logging.Handler):
@cached_property
def bridge(self):
return Bridge()
def emit(self, record):
msg = self.format(record)
self.bridge.log.emit(msg)

if __name__ == "__main__":
app = QCoreApplication()
handler = MyLogHandler()
handler.bridge.log.connect(print)
logging.getLogger().addHandler(handler)
QTimer.singleShot(1000, lambda: logging.warning("logging test"))
QTimer.singleShot(2000, QCoreApplication.quit)
app.exec_()

更新:

import logging
import sys
from PySide2.QtCore import QObject, QTimer, QUrl, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

class Main(QObject):
log = Signal(str)

class QLogHandler(logging.Handler):
def __init__(self, emitter):
super().__init__()
self._emitter = emitter
@property
def emitter(self):
return self._emitter
def emit(self, record):
msg = self.format(record)
self.emitter.log.emit(msg)

def configure_logging(*, handlers):
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=handlers,
)

def main():
app = QGuiApplication()
main = Main()
configure_logging(
handlers=[
logging.FileHandler("debug.log", mode="w"),
logging.StreamHandler(),
QLogHandler(main),
]
)
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("main", main)
engine.load(QUrl("Main3.qml"))
QTimer.singleShot(1000, lambda: logging.warning("logging test"))
ret = app.exec_()
sys.exit(ret)

if __name__ == "__main__":
main()

添加我最终使用的解决方案,因为我发现整个问题很难解析和应用。

import logging
from PySide6.QtWidgets import QPlainTextEdit
import PySide6.QtCore as QtCore
# Logging handler which receives any log created using the logging module.
# Contains a QPlainTextEdit which is automatically updated with logs.
class QTextEditLogger(logging.Handler):
class Emitter(QtCore.QObject):
log = QtCore.Signal(str)
def __init__(self, parent):
super().__init__()
# create text edit widget
self.widget = QPlainTextEdit(parent)
self.widget.setReadOnly(True)
# Create a QObject which will emit a signal for each log. This implicitly queues each 
# appendPlainText() call which makes it thread-safe
self.emitter = QTextEditLogger.Emitter()
self.emitter.log.connect(self.widget.appendPlainText)
# override Handler's emit method (this happens to share a name with Qt's emit method.
# Don't get confused)
def emit(self, record):
msg = self.format(record)
self.emitter.log.emit(msg) # emit a signal containing the log (emit in the Qt sense)

最新更新