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:
4pragma ComponentBehavior: Bound
5import Charts
6import QtQuick
7
8Item {
9 width: 300; height: 200
10
11 PieChart {
12 id: chart
13 anchors.centerIn: parent
14 width: 100; height: 100
15
16 component Slice: PieSlice {
17 parent: chart
18 anchors.fill: parent
19 }
20
21 slices: [
22 Slice {
23 color: "red"
24 fromAngle: 0
25 angleSpan: 110
26 },
27 Slice {
28 color: "black"
29 fromAngle: 110
30 angleSpan: 50
31 },
32 Slice {
To do this, we replace the pieSlice
property in PieChart
with a
slices
property, declared as a class variable of the
ListProperty
type.
The ListProperty
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 ListProperty
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 ListProperty
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) 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/chapter5-listproperties example from Qt v5.x"""
from pathlib import Path
import sys
from PySide6.QtCore import Property
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.RenderHint.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.ResizeMode.SizeRootObjectToView)
view.engine().addImportPath(Path(__file__).parent)
view.loadFromModule("Charts", "App")
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)
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
pragma ComponentBehavior: Bound
import Charts
import QtQuick
Item {
width: 300; height: 200
PieChart {
id: chart
anchors.centerIn: parent
width: 100; height: 100
component Slice: PieSlice {
parent: chart
anchors.fill: parent
}
slices: [
Slice {
color: "red"
fromAngle: 0
angleSpan: 110
},
Slice {
color: "black"
fromAngle: 110
angleSpan: 50
},
Slice {
color: "blue"
fromAngle: 160
angleSpan: 100
}
]
}
}
module Charts
typeinfo chapter5-listproperties.qmltypes
depends QtQuick
App 254.0 App.qml