JCowfer
JCowfer

Reputation: 140

Decorating a inherited mixin method as a pyqtSlot

I have a LogMixIn class. I want to create a Logger class that I can expose to QML for my QML to use. Since all the methods I want to use are from LogMixIn, is there a way to decorate inherited methods instead of having to redefine them like I do in the example below?

my_logging_lib.py

class LogMixIn():

    def logDebug(self, msg):
        # Log msg at Debug Severity

qml_logger.py

from PyQt5.QtCore import QObject, pyqtSlot
from my_logging_lib import LogMixIn

class QmlLogger(QObject, LogMixIn):

    @pyqtSlot(str)
    def logDebug(self, msg):
        super().logDebug(msg)

note 1: LogMixIn is use in a large portion of non-UI code so it's desireable to keep it QT agnostic.

Upvotes: 1

Views: 364

Answers (1)

ekhumoro
ekhumoro

Reputation: 120688

In PyQt5, pyqtSlot can decorate methods of classes that do not inherit from QObject, such as ordinary Python mixin classes. You can easily check this by examining the meta-object of a subclass that inherits such a mixin:

from PyQt5.QtCore import pyqtSlot, QObject, QMetaMethod

class LogMixIn:
    def logDebug(self, msg):
        pass

    @pyqtSlot(str)
    def logDebugEx(self, msg):
        pass

class QmlLogger(QObject, LogMixIn):
    @pyqtSlot(str)
    def logDebug(self, msg):
        super().logDebug(msg)

foo = QmlLogger()

meta = foo.metaObject()

for index in range(meta.methodCount()):
    method = meta.method(index)
    if method.methodType() == QMetaMethod.Slot:
        print(method.methodSignature())

Output:

b'deleteLater()'
b'_q_reregisterTimers(void*)'
b'logDebug(QString)'
b'logDebugEx(QString)'

As you can see, it makes no difference how the slots are defined. This is because PyQt does not need to check the type of the containing class until an actual connection is made. If the receiver is found to be a QObject at that time, PyQt can create an internal proxy QObject with a slot that can be used by Qt. For QML, the only thing that matters is that the methods are accessible via the meta-object system so that they can be called via e.g. invokeMethod.

Note that pyqtSignal and pyqtProperty can also be used in mixins. However, this behaviour was only introduced in PyQt5, so it won't work at all in PyQt4. I believe that similar behaviour is found in all versions of PySide, though.

UPDATE:

If you cannot decorate the mixin directly, the slots can be created within the subclass definition like this:

class QmlLogger(QObject, LogMixIn):
    logDebug = pyqtSlot(str)(LogMixIn.logDebug)

Upvotes: 1

Related Questions