扩展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
语句将 chartB
的 color
值绑定到 chartA
的 color
。每当 chartA
的 color
值发生变化时,chartB
的 color
值也会更新为相同的值。当窗口被点击时,MouseArea
中的 onClicked
处理程序会更改 chartA
的颜色,从而将两个图表的颜色更改为蓝色。
为color
属性启用属性绑定很容易。我们向其Property
装饰器添加一个notify
参数,以指示每当值更改时都会发出colorChanged
信号。
39
21
22@QmlElement
23class PieChart (QQuickPaintedItem):
24
25 chartCleared = Signal()
26 nameChanged = Signal()
然后,我们在setColor()
中发出这个信号:
43
44 @color.setter
45 def color(self, value):
46 if value != self._color:
47 self._color = value
48 self.update()
在发出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
from __future__ import annotations
"""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.RenderHint.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.Status.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)