Extending QML - Using Custom Property Types¶
This is the fourth of a series of 6 examples forming a tutorial about extending QML with Python.
The PieChart
type currently has a string-type property and a color-type property.
It could have many other types of properties. For example, it could have an
int-type property to store an identifier for each chart:
class PieChart(QQuickPaintedItem):
chartIdChanged = Signal()
@Property(int, notify=chartIdChanged)
def chartId(self):
pass
@chartId.setter
def setChartId(self, chartId):
pass
// QML
PieChart {
...
chartId: 100
}
Aside from int
, we could use various other property types. Many of the Qt
data types such as QColor
, QSize
and QRect
are automatically
supported from QML.
If we want to create a property whose type is not supported by QML by default, we need to register the type with the QML engine.
For example, let’s replace the use of the property
with a type called
PieSlice
that has a color
property. Instead of assigning a color,
we assign an PieSlice
value which itself contains a color
:
4import Charts
5import QtQuick
6
7Item {
8 width: 300; height: 200
9
10 PieChart {
11 id: chart
12 anchors.centerIn: parent
13 width: 100; height: 100
14
15 pieSlice: PieSlice {
16 anchors.fill: parent
17 color: "red"
18 }
19 }
20
21 Component.onCompleted: console.log("The pie is colored " + chart.pieSlice.color)
22}
Like PieChart
, this new PieSlice
type inherits from
QQuickPaintedItem
, is exposed via @QmlElement
and declares
its properties with the Property
decorator:
21
22
23@QmlElement
24class PieSlice (QQuickPaintedItem):
25
26 def __init__(self, parent=None):
27 QQuickPaintedItem.__init__(self, parent)
28 self._color = QColor()
29
30 @Property(QColor, final=True)
31 def color(self):
32 return self._color
33
34 @color.setter
35 def color(self, value):
36 self._color = value
37
38 def paint(self, painter):
39 pen = QPen(self._color, 2)
40 painter.setPen(pen)
To use it in PieChart
, we modify the color
property declaration
and associated method signatures:
58 self._name = value
59
60 @Property(PieSlice, final=True)
61 def pieSlice(self):
62 return self._pieSlice
63
64 @pieSlice.setter
65 def pieSlice(self, value):
There is one thing to be aware of when implementing setPieSlice()
. The
PieSlice
is a visual item, so it must be set as a child of the PieChart
using setParentItem()
so that the PieChart
knows to paint
this child item when its contents are drawn.
As with PieChart
, we add the Charts
type namespace, version 1.0:
15from PySide6.QtQuick import QQuickPaintedItem, QQuickView, QQuickItem
16
17# To be used on the @QmlElement decorator
18# (QML_IMPORT_MINOR_VERSION is optional)
// 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 {
id: chart
anchors.centerIn: parent
width: 100; height: 100
pieSlice: PieSlice {
anchors.fill: parent
color: "red"
}
}
Component.onCompleted: console.log("The pie is colored " + chart.pieSlice.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/chapter4-customPropertyTypes 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
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()
@Property(QColor, final=True)
def color(self):
return self._color
@color.setter
def color(self, value):
self._color = 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), 90 * 16, 290 * 16)
@QmlElement
class PieChart (QQuickItem):
def __init__(self, parent=None):
QQuickItem.__init__(self, parent)
self._name = None
self._pieSlice = None
@Property(str, final=True)
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@Property(PieSlice, final=True)
def pieSlice(self):
return self._pieSlice
@pieSlice.setter
def pieSlice(self, value):
self._pieSlice = value
self._pieSlice.setParentItem(self)
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)