РЕШЕННО: см. Мой ответ ниже. Я не могу принять в течение 2 дней.
У меня есть объект Python, полученный из QObject, методы которого я вызываю из QML. Эти методы, конечно, украшены @pyqtSlot. Однако есть проблема, когда я стараюсь сочетаться с моими собственными декораторами, например:
@pyqtSlot()
@decorator
def call(self):
print('Called!')
Я получаю file:X.qml:99: TypeError: Property 'call' of object X(0x7fed4c07fa50) is not a function
ошибки file:X.qml:99: TypeError: Property 'call' of object X(0x7fed4c07fa50) is not a function
Это декоратор, который я использую:
def decorator(f):
def wrapped(self):
print('get decorated')
return f(self)
return wrapped
Может ли кто-нибудь объяснить, почему это не работает? Некоторое поведение PyQt? Или я делаю что-то неправильно?
ПРИМЕЧАНИЕ. Он будет работать, если декоратор просто возвращает f
, проблемы возникают, когда декоратор возвращает вложенную функцию, такую как wrapped
.
Использование библиотеки декораторов не создает проблем, в следующей части приведен пример:
main.py
import sys
from PyQt5 import QtCore, QtGui, QtQml
import decorator
@decorator.decorator
def foo_decorator(f, *args, **kwargs):
print('get decorated')
return f(*args, **kwargs)
class Helper(QtCore.QObject):
@foo_decorator
@QtCore.pyqtSlot()
def call(self):
print('Called!')
if __name__ == "__main__":
app = QtGui.QGuiApplication(sys.argv)
obj = Helper()
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("obj", obj)
engine.load(QtCore.QUrl.fromLocalFile('main.qml'))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
main.qml
import QtQuick 2.11
import QtQuick.Controls 2.4
ApplicationWindow {
title: "Hello World"
width: 640
height: 480
visible: true
Component.onCompleted: obj.call()
}
Выход:
get decorated
Called!
Хорошо, с помощью @eyllanesc ответ я смог дойти до корня проблемы: это то, что декоратор, который я использовал, не сохранял подпись функции моего слота.
Простое изменение моего декоратора на это устраняет проблему:
def decorator(f):
def call(self):
print('get decorated')
return f(self)
return wrapped
Однако этот декоратор не очень многократно используется из-за имени функции жесткой кодировки. "Правильный" (только с использованием стандартных библиотек) способ исправить это с помощью декоратора wraps
из functools
:
from functools import wraps
def decorator(f):
@wraps(f)
def any_name_you_want(self):
print('get decorated')
return f(self)
return any_name_you_want
Попытайся:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
def decorator(f):
def wrapped(self):
print('get decorated')
return f(self)
return wrapped
class App(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt5 button")
self.setGeometry(500, 100, 320, 200)
button = QPushButton(' \n PyQt5\n button\n ', self)
button.setIcon(QIcon("E:/_Qt/img/qt-logo.png"))
button.setIconSize(QSize(34, 34))
button.setToolTip('This is an example button')
button.setFlat(True)
button.move(120,70)
button.clicked.connect(self.on_click)
@pyqtSlot()
@decorator
def on_click(self):
print('PyQt5 button click')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
return f ()
чтобыreturn f (self)
return f(self)