python - 定义后将 Qt 属性附加到 Python 类;无法从 QML 访问
问题描述
我正在使用 PySide2 和 QML。当我QtCore.Property
在 Python 对象中定义 a 时,它成功地为 QML 以及 Python 属性 get/set 方法设置了访问权限。但是,我有一种情况,在定义类之后附加属性会很有用,但这并没有像我预期的那样工作。
首先,一个有效的例子:
import sys
from PySide2.QtCore import QObject, Property, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
class Person(QObject):
def get_name(self):
print('trying to access name')
return self._name
def set_name(self, new_name):
print('trying to set name')
self._name = new_name
name = Property(str, get_name, set_name)
bob = Person()
bob.name = 'Bob'
print(f'Name: "{bob.name}"')
print(bob.__dict__)
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty('backend', bob)
engine.load("example.qml")
sys.exit(app.exec_())
还有一个要查看的简单 QML 文件:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
visible: true
Component.onCompleted: { console.debug(JSON.stringify(backend)) }
Text { text: backend.name }
}
生成的图形窗口显示“Bob”,控制台输出包括:
trying to set name
trying to access name
Name: "Bob"
{'_name': 'Bob'}
qml: {"objectName":"","name":"Bob"}
然而,看似简单的改变,事情就变得混乱了。我将 Python 类定义替换为:
class Person(QObject):
pass
def get_name(obj):
print('trying to access name')
return obj._name
def set_name(obj, new_name):
print('trying to set name')
obj._name = new_name
Person.name = Property(str, get_name, set_name)
也就是说,getter、setter 和 Property 是在类定义之外定义的,并在事后附加到它上面。奇怪的是QML看不到Property,但是Python还是知道它的正确用法:
trying to set name
trying to access name
Name: "Bob"
{'_name': 'Bob'}
qml: {"objectName":""}
file:///Users/charles/Projects/qt/property-test/example.qml:9:9: Unable to assign [undefined] to QString
QtCore.Property
对于如何正确设置 Python 属性,而不是 QML 属性,我摸不着头脑。我对它的实施有什么遗漏吗?
解决方案
TL; 博士;QProperties 不能在运行时定义。
QML 不使用 Python 自省来访问QObject
s 属性,而是 Qt 在 中实现了自己的自省QMetaObject
,可以在以下代码中看到:
class Person(QObject):
def get_name(self):
return self._name
def set_name(self, new_name):
self._name = new_name
name = Property(str, get_name, set_name)
bob = Person()
bob.name = "Bob"
print("qproperties:")
print("============")
mo = bob.metaObject()
for i in range(mo.propertyOffset(), mo.propertyCount()):
prop = mo.property(i)
print(f"{i}: {prop.name()} = {prop.read(bob)}")
输出:
qproperties:
============
1: name = Bob
class Person(QObject):
pass
def get_name(obj):
return obj._name
def set_name(obj, new_name):
obj._name = new_name
Person.name = Property(str, get_name, set_name)
bob = Person()
bob.name = "Bob"
print("qproperties:")
print("============")
mo = bob.metaObject()
for i in range(mo.propertyOffset(), mo.propertyCount()):
prop = mo.property(i)
print(f"{i}: {prop.name()} = {prop.read(bob)}")
输出:
qproperties:
============
PySide2(也叫PyQt5)在构建类时会创建QProperties,所以如果在类构建后添加QProperties,则不会添加QMetaObject。
另一种方法是使用QQmlPropertyMap
:
import sys
from PySide2.QtCore import QTimer
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, QQmlPropertyMap
person_map = QQmlPropertyMap()
person_map.insert("name", "Bob")
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("backend", person_map)
engine.load("example.qml")
QTimer.singleShot(1000, lambda: person_map.setProperty("name", "Joe"))
sys.exit(app.exec_())
推荐阅读
- c - 在 C 中返回错误:-1073741819 (0xC0000005)
- arrays - Ruby - 如何编写一个接受具有不同数量参数的过程的方法?
- vue.js - 您能帮我解决我的 vue 代码中的落后问题吗?
- python - Python 3.7 输入在线程中的使用
- python - 将 pd.cut-list 中的连续元素与新的直方图标签列表配对?
- mysql - mysql将同一字符串上的多个值转换为数字总和
- python - 如何将多个 ParentalKeys 设置为 Wagtail TaggedItemBase 标签类
- javascript - 如何将数据对象重组为对象数组?
- php - 在简单的 html dom 中按类和 id 选择在谷歌搜索中不起作用
- python - 如何从python selenium中的只读框中复制文本