扩展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 语句将 chartBcolor 值绑定到 chartAcolor。每当 chartAcolor 值发生变化时,chartBcolor 值也会更新为相同的值。当窗口被点击时,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)