Extending QML - Using List Property Types#
This is the fifth of a series of 6 examples forming a tutorial about extending QML with Python.
Right now, a PieChart
can only have one PieSlice.
Ideally a chart would
have multiple slices, with different colors and sizes. To do this, we could
have a slices
property that accepts a list of PieSlice
items:
4import Charts
5import QtQuick
6
7Item {
8 width: 300; height: 200
9
10 PieChart {
11 anchors.centerIn: parent
12 width: 100; height: 100
13
14 slices: [
15 PieSlice {
16 anchors.fill: parent
17 color: "red"
18 fromAngle: 0; angleSpan: 110
19 },
20 PieSlice {
21 anchors.fill: parent
22 color: "black"
23 fromAngle: 110; angleSpan: 50
24 },
25 PieSlice {
26 anchors.fill: parent
27 color: "blue"
28 fromAngle: 160; angleSpan: 100
29 }
30 ]
31 }
32}
To do this, we replace the pieSlice
property in PieChart
with a
slices
property, declared as a class variable of the QQmlListProperty
type. The QQmlListProperty
class enables the creation of list properties in
QML extensions. We replace the pieSlice()
function with a slices()
function that returns a list of slices, and add an internal appendSlice()
function (discussed below). We also use a list to store the internal list of
slices as _slices
:
62class PieChart (QQuickItem):
63 def __init__(self, parent=None):
64 QQuickItem.__init__(self, parent)
65 self._name = u''
75
76 def appendSlice(self, _slice):
77 _slice.setParentItem(self)
78 self._slices.append(_slice)
79
Although the slices
property does not have an associated setter, it is
still modifiable because of the way QQmlListProperty
works. We indicate
that the internal PieChart.appendSlice()
function is to be called whenever
a request is made from QML to add items to the list.
The appendSlice()
function simply sets the parent item as before, and adds
the new item to the _slices
list. As you can see, the append function for
a QQmlListProperty
is called with two arguments: the list property, and the
item that is to be appended.
The PieSlice
class has also been modified to include fromAngle
and
angleSpan
properties and to draw the slice according to these values. This
is a straightforward modification if you have read the previous pages in this
tutorial, so the code is not shown here.
// 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
PieChart {
anchors.centerIn: parent
width: 100; height: 100
slices: [
PieSlice {
anchors.fill: parent
color: "red"
fromAngle: 0; angleSpan: 110
},
PieSlice {
anchors.fill: parent
color: "black"
fromAngle: 110; angleSpan: 50
},
PieSlice {
anchors.fill: parent
color: "blue"
fromAngle: 160; angleSpan: 100
}
]
}
}
# 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/chapter5-listproperties example from Qt v5.x"""
import os
from pathlib import Path
import sys
from PySide6.QtCore import Property, QUrl
from PySide6.QtGui import QGuiApplication, QPen, QPainter, QColor
from PySide6.QtQml import QmlElement, ListProperty
from PySide6.QtQuick import QQuickPaintedItem, QQuickView, QQuickItem
# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
QML_IMPORT_NAME = "Charts"
QML_IMPORT_MAJOR_VERSION = 1
@QmlElement
class PieSlice (QQuickPaintedItem):
def __init__(self, parent=None):
QQuickPaintedItem.__init__(self, parent)
self._color = QColor()
self._fromAngle = 0
self._angleSpan = 0
@Property(QColor, final=True)
def color(self):
return self._color
@color.setter
def color(self, value):
self._color = value
@Property(int, final=True)
def fromAngle(self):
return self._angle
@fromAngle.setter
def fromAngle(self, value):
self._fromAngle = value
@Property(int, final=True)
def angleSpan(self):
return self._angleSpan
@angleSpan.setter
def angleSpan(self, value):
self._angleSpan = value
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), self._fromAngle * 16, self._angleSpan * 16)
@QmlElement
class PieChart (QQuickItem):
def __init__(self, parent=None):
QQuickItem.__init__(self, parent)
self._name = u''
self._slices = []
@Property(str, final=True)
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
def appendSlice(self, _slice):
_slice.setParentItem(self)
self._slices.append(_slice)
slices = ListProperty(PieSlice, appendSlice, final=True)
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)