扩展QML - 添加属性绑定#
这是关于使用Python扩展QML的6个示例教程系列的第三篇。
属性绑定是QML的强大功能,它允许自动同步不同类型的值。它在属性值更改时使用信号通知并更新其他类型值的值。
让我们为color
属性启用属性绑定。这意味着如果我们有这样的代码
7Item {
8 width: 300; height: 200
9
10 Row {
11 anchors.centerIn: parent
12 spacing: 20
13
14 PieChart {
15 id: chartA
16 width: 100; height: 100
17 color: "red"
18 }
19
20 PieChart {
21 id: chartB
22 width: 100; height: 100
23 color: chartA.color
24 }
25 }
26
27 MouseArea {
28 anchors.fill: parent
29 onClicked: { chartA.color = "blue" }
30 }
31
32 Text {
33 anchors {
34 bottom: parent.bottom;
35 horizontalCenter: parent.horizontalCenter;
36 bottomMargin: 20
37 }
38 text: "Click anywhere to change the chart color"
39 }
40}
语句 color: chartA.color
将图表B的 color
值绑定到图表A的 color
上。每当图表A的 color
值发生变化时,图表B的 color
值也会更新为相同的值。当窗口被点击时,MouseArea
中的 onClicked
处理程序会改变图表A的颜色,从而将两个图表的颜色都更改为蓝色。
为 color
属性启用属性绑定很容易。我们在其 Property
装饰器中添加一个 notify
参数,以指示每当值发生变化时都会发出 colorChanged
信号。
39 @Property(QColor, notify=colorChanged, final=True)
21@QmlElement
22class PieChart (QQuickPaintedItem):
23
24 chartCleared = Signal()
25 nameChanged = Signal()
26 colorChanged = Signal()
然后,我们在 setColor()
中发出这个信号
43 @color.setter
44 def color(self, value):
45 if value != self._color:
46 self._color = value
47 self.update()
48 self.colorChanged.emit()
在发出 colorChanged().
之前,确保 setColor()
检查颜色值是否实际上发生了变化。这确保信号不会被无谓地发出,并且也防止其他类型响应值变化时的循环。
绑定在 QML 中使用是必不可少的。如果您能实现,应始终为属性添加 notify
信号,以便您的属性可以在绑定中使用。无法绑定的属性不能自动更新,并且不能在 QML 中灵活使用。此外,由于绑定在 QML 使用中频繁调用,并且被视为依赖,因此如果未实现绑定,您的自定义 QML 类型的用户可能会看到意外的行为。
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import Charts
import QtQuick
Item {
width: 300; height: 200
Row {
anchors.centerIn: parent
spacing: 20
PieChart {
id: chartA
width: 100; height: 100
color: "red"
}
PieChart {
id: chartB
width: 100; height: 100
color: chartA.color
}
}
MouseArea {
anchors.fill: parent
onClicked: { chartA.color = "blue" }
}
Text {
anchors {
bottom: parent.bottom;
horizontalCenter: parent.horizontalCenter;
bottomMargin: 20
}
text: "Click anywhere to change the chart color"
}
}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
"""PySide6 port of the qml/tutorials/extending-qml/chapter3-bindings example from Qt v5.x"""
import os
from pathlib import Path
import sys
from PySide6.QtCore import Property, Signal, Slot, QUrl, Qt
from PySide6.QtGui import QGuiApplication, QPen, QPainter, QColor
from PySide6.QtQml import QmlElement
from PySide6.QtQuick import QQuickPaintedItem, QQuickView
# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
QML_IMPORT_NAME = "Charts"
QML_IMPORT_MAJOR_VERSION = 1
@QmlElement
class PieChart (QQuickPaintedItem):
chartCleared = Signal()
nameChanged = Signal()
colorChanged = Signal()
def __init__(self, parent=None):
QQuickPaintedItem.__init__(self, parent)
self._name = u''
self._color = QColor()
def paint(self, painter):
pen = QPen(self._color, 2)
painter.setPen(pen)
painter.setRenderHints(QPainter.Antialiasing, True)
painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
@Property(QColor, notify=colorChanged, final=True)
def color(self):
return self._color
@color.setter
def color(self, value):
if value != self._color:
self._color = value
self.update()
self.colorChanged.emit()
@Property(str, notify=nameChanged, final=True)
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@Slot() # This should be something like @Invokable
def clearChart(self):
self.color = Qt.transparent
self.update()
self.chartCleared.emit()
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
res = app.exec()
# Deleting the view before it goes out of scope is required to make sure all child QML instances
# are destroyed in the correct order.
del view
sys.exit(res)