Issue
I am trying to send a Qt signal every time the logging handler emit function is called. But I think MyLogHandler.emit and log.emit functions are conflicting.
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')
Error:
TypeError: emit() takes 2 positional arguments but 3 were given
UPDATE:
I have tried to use composition (per @eyllanesc), but I still can't connect the signal to the QML file. I am not sure why I can't receive the signal in QML. It doesn't seem like it is emitting anything. What am I doing wrong?
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
}
}
}
Solution
The problem is that both base classes have an emit()
method that causes that collision. A workaround is not to use inheritance but composition:
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_()
Update:
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()
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.