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 the QmlElement
decorator and declares
its properties with the Property
decorator:
21
22@QmlElement
23class PieSlice (QQuickPaintedItem):
24
25 def __init__(self, parent=None):
26 QQuickPaintedItem.__init__(self, parent)
27 self._color = QColor()
28
29 @Property(QColor, final=True)
30 def color(self):
31 return self._color
32
33 @color.setter
34 def color(self, value):
35 self._color = value
36
37 def paint(self, painter):
38 pen = QPen(self._color, 2)
39 painter.setPen(pen)
40 painter.setRenderHints(QPainter.Antialiasing, True)
To use it in PieChart
, we modify the color
property declaration
and associated method signatures:
58
59 @Property(PieSlice, final=True)
60 def pieSlice(self):
61 return self._pieSlice
62
63 @pieSlice.setter
64 def pieSlice(self, value):
65 self._pieSlice = 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 QQuickItem.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:
15
16# To be used on the @QmlElement decorator
17# (QML_IMPORT_MINOR_VERSION is optional)
18QML_IMPORT_NAME = "Charts"
// 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
"""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.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.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)