aboutsummaryrefslogtreecommitdiffstats
path: root/examples/qml
diff options
context:
space:
mode:
Diffstat (limited to 'examples/qml')
-rw-r--r--examples/qml/editingmodel/MovingRectangle.qml78
-rw-r--r--examples/qml/editingmodel/doc/editingmodel.rst16
-rw-r--r--examples/qml/editingmodel/doc/qabstractlistmodelqml.pngbin0 -> 45810 bytes
-rw-r--r--examples/qml/editingmodel/main.py21
-rw-r--r--examples/qml/editingmodel/main.pyproject3
-rw-r--r--examples/qml/editingmodel/main.qml106
-rw-r--r--examples/qml/editingmodel/model.py156
-rw-r--r--examples/qml/signals/pytoqml1/main.py31
-rw-r--r--examples/qml/signals/pytoqml1/pytoqml1.pyproject3
-rw-r--r--examples/qml/signals/pytoqml1/view.qml36
-rw-r--r--examples/qml/signals/pytoqml2/main.py54
-rw-r--r--examples/qml/signals/pytoqml2/pytoqml2.pyproject3
-rw-r--r--examples/qml/signals/pytoqml2/view.qml30
-rw-r--r--examples/qml/signals/qmltopy1/main.py50
-rw-r--r--examples/qml/signals/qmltopy1/qmltopy1.pyproject3
-rw-r--r--examples/qml/signals/qmltopy1/view.qml54
-rw-r--r--examples/qml/signals/qmltopy2/main.py45
-rw-r--r--examples/qml/signals/qmltopy2/qmltopy2.pyproject3
-rw-r--r--examples/qml/signals/qmltopy2/view.qml49
-rw-r--r--examples/qml/signals/qmltopy3/main.py33
-rw-r--r--examples/qml/signals/qmltopy3/qmltopy3.pyproject3
-rw-r--r--examples/qml/signals/qmltopy3/view.qml66
-rw-r--r--examples/qml/signals/qmltopy4/main.py33
-rw-r--r--examples/qml/signals/qmltopy4/qmltopy4.pyproject3
-rw-r--r--examples/qml/signals/qmltopy4/view.qml31
-rw-r--r--examples/qml/textproperties/doc/textproperties.pngbin0 -> 14082 bytes
-rw-r--r--examples/qml/textproperties/doc/textproperties.rst9
-rw-r--r--examples/qml/textproperties/main.py76
-rw-r--r--examples/qml/textproperties/textproperties.pyproject3
-rw-r--r--examples/qml/textproperties/view.qml160
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/People/Main.qml9
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/adding.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/doc/adding.rst67
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/main.py30
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/adding/person.py34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/advanced1-Base-project.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py46
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/doc/advanced1-Base-project.rst57
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py33
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.py41
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/advanced2-Inheritance-and-coercion.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.py46
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/doc/advanced2-Inheritance-and-coercion.rst62
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py38
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.py52
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/Main.qml15
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/advanced3-Default-properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/doc/advanced3-Default-properties.rst40
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py38
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/Main.qml33
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/advanced4-Grouped-properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/doc/advanced4-Grouped-properties.rst39
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py46
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.py102
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/Main.qml22
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/advanced5-Attached-properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.py71
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/doc/advanced5-Attached-properties.rst51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py48
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/Main.qml27
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/advanced6-Property-value-source.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.py89
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/doc/advanced6-Property-value-source.rst43
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.py49
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py53
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.py51
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/People/Main.qml29
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/binding.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/birthdayparty.py83
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/doc/binding.rst17
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/happybirthdaysong.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/main.py52
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/binding/person.py53
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/doc/extended.rst41
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/example.qml8
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/extended.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/extended/main.py95
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/People/Main.qml19
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/birthdayparty.py47
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/doc/methods.rst15
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/main.py32
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/methods.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/methods/person.py34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/People/Main.qml16
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/People/qmldir3
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/birthdayparty.py41
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/doc/properties.rst89
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/main.py34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/person.py34
-rw-r--r--examples/qml/tutorials/extending-qml-advanced/properties/properties.pyproject4
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/app.qml26
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/basics.py68
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter1-basics/doc/chapter1-basics.rst107
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/app.qml32
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/doc/chapter2-methods.rst36
-rw-r--r--examples/qml/tutorials/extending-qml/chapter2-methods/methods.py75
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml40
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/bindings.py79
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter3-bindings/doc/chapter3-bindings.rst54
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml22
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py83
-rw-r--r--examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/doc/chapter4-customPropertyTypes.rst73
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml32
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst47
-rw-r--r--examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py97
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py41
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py53
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/plugins.pngbin0 -> 5014 bytes
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml32
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/chapter6-plugins.pyproject3
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst26
-rw-r--r--examples/qml/tutorials/extending-qml/chapter6-plugins/doc/plugins.pngbin0 -> 5014 bytes
-rw-r--r--examples/qml/usingmodel/doc/usingmodel.pngbin0 -> 2791 bytes
-rw-r--r--examples/qml/usingmodel/doc/usingmodel.rst11
-rw-r--r--examples/qml/usingmodel/usingmodel.py74
-rw-r--r--examples/qml/usingmodel/usingmodel.pyproject3
-rw-r--r--examples/qml/usingmodel/view.qml21
137 files changed, 4727 insertions, 0 deletions
diff --git a/examples/qml/editingmodel/MovingRectangle.qml b/examples/qml/editingmodel/MovingRectangle.qml
new file mode 100644
index 000000000..b99a5f4dc
--- /dev/null
+++ b/examples/qml/editingmodel/MovingRectangle.qml
@@ -0,0 +1,78 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ id: root
+ property int modelIndex
+ property Item dragParent
+ property Item sizeParent
+ property alias text: zone.text
+ property alias bgColor: root.color
+
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+ color: backgroundColor
+ anchors.fill: sizeParent
+ border.color: "yellow"
+ border.width: 0
+ TextArea {
+ id: zone
+ anchors.centerIn: parent
+ text: display
+ onTextChanged: model.edit = text
+ }
+
+ MouseArea {
+ id: zoneMouseArea
+ anchors.fill: parent
+
+ acceptedButtons: Qt.MiddleButton
+ onClicked: function(mouse) {
+ if (mouse.button == Qt.MiddleButton)
+ lv.model.remove(index)
+ else
+ mouse.accepted = false
+ }
+ }
+ DragHandler {
+ id: dragHandler
+ xAxis {
+
+ enabled: true
+ minimum: 0
+ maximum: lv.width - droparea.width
+ }
+ yAxis.enabled: false
+ acceptedButtons: Qt.LeftButton
+ }
+ Drag.active: dragHandler.active
+ Drag.source: root
+ Drag.hotSpot.x: width / 2
+
+ states: [
+ State {
+ when: dragHandler.active
+ ParentChange {
+ target: root
+ parent: root.dragParent
+ }
+
+ AnchorChanges {
+ target: root
+ anchors.horizontalCenter: undefined
+ anchors.verticalCenter: undefined
+ }
+ PropertyChanges {
+ target: root
+ opacity: 0.6
+ border.width: 3
+ }
+ }
+ ]
+}
diff --git a/examples/qml/editingmodel/doc/editingmodel.rst b/examples/qml/editingmodel/doc/editingmodel.rst
new file mode 100644
index 000000000..2b45b23f0
--- /dev/null
+++ b/examples/qml/editingmodel/doc/editingmodel.rst
@@ -0,0 +1,16 @@
+QAbstractListModel in QML
+=========================
+
+.. tags:: Android
+
+This example shows how to add, remove and move items inside a QML
+ListView, but showing and editing the data via roles using a
+QAbstractListModel from Python.
+
+You can add new elements and reset the view using the two top buttons,
+remove elements by 'middle click' the element, and move the elements
+with a 'left click' plus dragging the item around.
+
+.. image:: qabstractlistmodelqml.png
+ :width: 400
+ :alt: QAbstractListModel/ListView Screenshot
diff --git a/examples/qml/editingmodel/doc/qabstractlistmodelqml.png b/examples/qml/editingmodel/doc/qabstractlistmodelqml.png
new file mode 100644
index 000000000..6e181fba1
--- /dev/null
+++ b/examples/qml/editingmodel/doc/qabstractlistmodelqml.png
Binary files differ
diff --git a/examples/qml/editingmodel/main.py b/examples/qml/editingmodel/main.py
new file mode 100644
index 000000000..5240a9de0
--- /dev/null
+++ b/examples/qml/editingmodel/main.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtCore import QUrl
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from model import BaseModel # noqa: F401
+
+if __name__ == "__main__":
+ app = QGuiApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+ qml_file = Path(__file__).parent / "main.qml"
+ engine.load(QUrl.fromLocalFile(qml_file))
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+ sys.exit(app.exec())
diff --git a/examples/qml/editingmodel/main.pyproject b/examples/qml/editingmodel/main.pyproject
new file mode 100644
index 000000000..71272a973
--- /dev/null
+++ b/examples/qml/editingmodel/main.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["model.py","main.qml","main.py","MovingRectangle.qml"]
+}
diff --git a/examples/qml/editingmodel/main.qml b/examples/qml/editingmodel/main.qml
new file mode 100644
index 000000000..2318ae8b3
--- /dev/null
+++ b/examples/qml/editingmodel/main.qml
@@ -0,0 +1,106 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Window
+import BaseModel
+
+Window {
+ title: "Moving Rectangle"
+ width: 800
+ height: 480
+ visible: true
+ id: mainWindow
+
+ Column {
+ spacing: 20
+ anchors.fill: parent
+ id: mainColumn
+ Text {
+ padding: 20
+ font.pointSize: 10
+ width: 600
+ wrapMode: Text.Wrap
+ text: "This example shows how to add, remove and move items inside a QML ListView.\n
+It shows and edits data via roles using QAbstractListModel on the Python side.\n
+Use the 'Middle click' on top of a rectangle to remove an item.\n
+'Left click' and drag to move the items."
+ }
+
+ Button {
+ anchors {
+ left: mainColumn.left
+ right: mainColumn.right
+ margins: 30
+ }
+ text: "Reset view"
+ onClicked: lv.model.reset()
+ }
+
+ Button {
+ anchors {
+ left: mainColumn.left
+ right: mainColumn.right
+ margins: 30
+ }
+ text: "Add element"
+ onClicked: lv.model.append()
+ }
+
+ ListView {
+ id: lv
+ anchors {
+ left: mainColumn.left
+ right: mainColumn.right
+ margins: 30
+ }
+
+ height: 200
+ model: BaseModel {}
+ orientation: ListView.Horizontal
+ displaced: Transition {
+ NumberAnimation {
+ properties: "x,y"
+ easing.type: Easing.OutQuad
+ }
+ }
+ delegate: DropArea {
+ id: droparea
+ width: ratio * lv.width
+ height: lv.height
+
+ onEntered: function (drag) {
+ let dragindex = drag.source.modelIndex
+ if (index === dragindex)
+ return
+ lv.model.move(dragindex, index)
+ }
+
+ MovingRectangle {
+ modelIndex: index
+ dragParent: lv
+ sizeParent: droparea
+ }
+ }
+
+ MouseArea {
+ id: lvMousearea
+ anchors.fill: lv
+ z: -1
+ }
+ Rectangle {
+ id: lvBackground
+ anchors.fill: lv
+ anchors.margins: -border.width
+ color: "white"
+ border.color: "black"
+ border.width: 5
+ z: -1
+ }
+ Component.onCompleted: {
+ lv.model.reset()
+ }
+ }
+ }
+}
diff --git a/examples/qml/editingmodel/model.py b/examples/qml/editingmodel/model.py
new file mode 100644
index 000000000..02a1e5717
--- /dev/null
+++ b/examples/qml/editingmodel/model.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, Qt, Slot
+from PySide6.QtGui import QColor
+from PySide6.QtQml import QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "BaseModel"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class BaseModel(QAbstractListModel):
+
+ RatioRole = Qt.UserRole + 1
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.db = []
+
+ def rowCount(self, parent=QModelIndex()):
+ return len(self.db)
+
+ def roleNames(self):
+ default = super().roleNames()
+ default[self.RatioRole] = QByteArray(b"ratio")
+ default[Qt.BackgroundRole] = QByteArray(b"backgroundColor")
+ return default
+
+ def data(self, index, role: int):
+ if not self.db:
+ ret = None
+ elif not index.isValid():
+ ret = None
+ elif role == Qt.DisplayRole:
+ ret = self.db[index.row()]["text"]
+ elif role == Qt.BackgroundRole:
+ ret = self.db[index.row()]["bgColor"]
+ elif role == self.RatioRole:
+ ret = self.db[index.row()]["ratio"]
+ else:
+ ret = None
+ return ret
+
+ def setData(self, index, value, role):
+ if not index.isValid():
+ return False
+ if role == Qt.EditRole:
+ self.db[index.row()]["text"] = value
+ return True
+
+ @Slot(result=bool)
+ def append(self):
+ """Slot to append a row at the end"""
+ return self.insertRow(self.rowCount())
+
+ def insertRow(self, row):
+ """Insert a single row at row"""
+ return self.insertRows(row, 0)
+
+ def insertRows(self, row: int, count, index=QModelIndex()):
+ """Insert n rows (n = 1 + count) at row"""
+
+ self.beginInsertRows(QModelIndex(), row, row + count)
+
+ # start database work
+ if len(self.db):
+ newid = max(x["id"] for x in self.db) + 1
+ else:
+ newid = 1
+ for i in range(count + 1): # at least one row
+ self.db.insert(
+ row, {"id": newid, "text": "new", "bgColor": QColor("purple"), "ratio": 0.2}
+ )
+ # end database work
+ self.endInsertRows()
+ return True
+
+ @Slot(int, int, result=bool)
+ def move(self, source: int, target: int):
+ """Slot to move a single row from source to target"""
+ return self.moveRow(QModelIndex(), source, QModelIndex(), target)
+
+ def moveRow(self, sourceParent, sourceRow, dstParent, dstChild):
+ """Move a single row"""
+ return self.moveRows(sourceParent, sourceRow, 0, dstParent, dstChild)
+
+ def moveRows(self, sourceParent, sourceRow, count, dstParent, dstChild):
+ """Move n rows (n=1+ count) from sourceRow to dstChild"""
+
+ if sourceRow == dstChild:
+ return False
+
+ elif sourceRow > dstChild:
+ end = dstChild
+
+ else:
+ end = dstChild + 1
+
+ self.beginMoveRows(QModelIndex(), sourceRow, sourceRow + count, QModelIndex(), end)
+
+ # start database work
+ pops = self.db[sourceRow: sourceRow + count + 1]
+ if sourceRow > dstChild:
+ self.db = (
+ self.db[:dstChild]
+ + pops
+ + self.db[dstChild:sourceRow]
+ + self.db[sourceRow + count + 1:]
+ )
+ else:
+ start = self.db[:sourceRow]
+ middle = self.db[dstChild: dstChild + 1]
+ endlist = self.db[dstChild + count + 1:]
+ self.db = start + middle + pops + endlist
+ # end database work
+
+ self.endMoveRows()
+ return True
+
+ @Slot(int, result=bool)
+ def remove(self, row: int):
+ """Slot to remove one row"""
+ return self.removeRow(row)
+
+ def removeRow(self, row, parent=QModelIndex()):
+ """Remove one row at index row"""
+ return self.removeRows(row, 0, parent)
+
+ def removeRows(self, row: int, count: int, parent=QModelIndex()):
+ """Remove n rows (n=1+count) starting at row"""
+ self.beginRemoveRows(QModelIndex(), row, row + count)
+
+ # start database work
+ self.db = self.db[:row] + self.db[row + count + 1:]
+ # end database work
+
+ self.endRemoveRows()
+ return True
+
+ @Slot(result=bool)
+ def reset(self):
+ self.beginResetModel()
+ self.resetInternalData() # should work without calling it ?
+ self.endResetModel()
+ return True
+
+ def resetInternalData(self):
+ self.db = [
+ {"id": 3, "bgColor": QColor("red"), "ratio": 0.15, "text": "first"},
+ {"id": 1, "bgColor": QColor("blue"), "ratio": 0.1, "text": "second"},
+ {"id": 2, "bgColor": QColor("green"), "ratio": 0.2, "text": "third"},
+ ]
diff --git a/examples/qml/signals/pytoqml1/main.py b/examples/qml/signals/pytoqml1/main.py
new file mode 100644
index 000000000..5bc27a521
--- /dev/null
+++ b/examples/qml/signals/pytoqml1/main.py
@@ -0,0 +1,31 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from pathlib import Path
+import sys
+from PySide6.QtCore import QTimer, QUrl
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+
+ timer = QTimer()
+ timer.start(2000)
+
+ view = QQuickView()
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
+ view.setSource(QUrl.fromLocalFile(qml_file))
+ if view.status() == QQuickView.Error:
+ sys.exit(-1)
+ root = view.rootObject()
+
+ timer.timeout.connect(root.updateRotater)
+
+ 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)
diff --git a/examples/qml/signals/pytoqml1/pytoqml1.pyproject b/examples/qml/signals/pytoqml1/pytoqml1.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/signals/pytoqml1/pytoqml1.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/signals/pytoqml1/view.qml b/examples/qml/signals/pytoqml1/view.qml
new file mode 100644
index 000000000..af2d966be
--- /dev/null
+++ b/examples/qml/signals/pytoqml1/view.qml
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: page
+
+ function updateRotater() {
+ rotater.angle = rotater.angle + 45
+ }
+
+ width: 500; height: 200
+ color: "lightgray"
+
+ Rectangle {
+ id: rotater
+ property real angle : 0
+ x: 240
+ width: 100; height: 10
+ color: "black"
+ y: 95
+
+ transform: Rotation {
+ origin.x: 10; origin.y: 5
+ angle: rotater.angle
+ Behavior on angle {
+ SpringAnimation {
+ spring: 1.4
+ damping: .05
+ }
+ }
+ }
+ }
+
+}
diff --git a/examples/qml/signals/pytoqml2/main.py b/examples/qml/signals/pytoqml2/main.py
new file mode 100644
index 000000000..3a3650aba
--- /dev/null
+++ b/examples/qml/signals/pytoqml2/main.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from pathlib import Path
+import sys
+from PySide6.QtCore import QObject, QTimer, QUrl, Signal, Slot
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+from PySide6.QtQml import QmlElement
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "examples.signals.pytoqml2"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class RotateValue(QObject):
+ valueChanged = Signal(int, arguments=['val'])
+
+ def __init__(self):
+ super().__init__()
+ self.r = 0
+
+ @Slot()
+ def increment(self):
+ self.r = self.r + 10
+ self.valueChanged.emit(self.r)
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+
+ rotatevalue = RotateValue()
+ timer = QTimer()
+ timer.start(2000)
+ view.setInitialProperties({"rotatevalue": rotatevalue})
+
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
+ view.setSource(QUrl.fromLocalFile(qml_file))
+ if view.status() == QQuickView.Error:
+ sys.exit(-1)
+
+ timer.timeout.connect(rotatevalue.increment)
+
+ 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)
diff --git a/examples/qml/signals/pytoqml2/pytoqml2.pyproject b/examples/qml/signals/pytoqml2/pytoqml2.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/signals/pytoqml2/pytoqml2.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/signals/pytoqml2/view.qml b/examples/qml/signals/pytoqml2/view.qml
new file mode 100644
index 000000000..2e9128f53
--- /dev/null
+++ b/examples/qml/signals/pytoqml2/view.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQml
+
+import examples.signals.pytoqml2 1.0
+
+Rectangle {
+ id: page
+
+ width: 500; height: 200
+ color: "lightgray"
+ required property RotateValue rotatevalue
+
+ Text {
+ id: helloText
+ text: "Hello world!"
+ anchors.horizontalCenter: page.horizontalCenter
+ y: 30
+ font.pointSize: 24; font.bold: true
+ }
+
+ Connections {
+ target: rotatevalue
+ function onValueChanged(val) {
+ helloText.rotation = val
+ }
+ }
+}
diff --git a/examples/qml/signals/qmltopy1/main.py b/examples/qml/signals/qmltopy1/main.py
new file mode 100644
index 000000000..83966903c
--- /dev/null
+++ b/examples/qml/signals/qmltopy1/main.py
@@ -0,0 +1,50 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from pathlib import Path
+import sys
+from PySide6.QtCore import QObject, QUrl, Slot
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+from PySide6.QtQml import QmlElement
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "examples.signals.qmltopy1"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class Console(QObject):
+ """Output stuff on the console."""
+
+ @Slot(str)
+ @Slot('double')
+ def output(self, s):
+ print(s)
+
+ @Slot(str)
+ def outputStr(self, s):
+ print(s)
+
+ @Slot('double')
+ def outputFloat(self, x):
+ print(x)
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.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)
diff --git a/examples/qml/signals/qmltopy1/qmltopy1.pyproject b/examples/qml/signals/qmltopy1/qmltopy1.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/signals/qmltopy1/qmltopy1.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/signals/qmltopy1/view.qml b/examples/qml/signals/qmltopy1/view.qml
new file mode 100644
index 000000000..b10e2e77d
--- /dev/null
+++ b/examples/qml/signals/qmltopy1/view.qml
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick
+
+import examples.signals.qmltopy1 1.0
+
+Rectangle {
+ id: page
+
+ width: 500; height: 200
+ color: "lightgray"
+
+ Console {
+ id: pyConsole
+ }
+
+ Text {
+ id: helloText
+ text: "Hello world!"
+ anchors.horizontalCenter: page.horizontalCenter
+ y: 30
+ font.pointSize: 24; font.bold: true
+ }
+
+ Rectangle {
+ id: button
+ width: 150; height: 40
+ color: "darkgray"
+ anchors.horizontalCenter: page.horizontalCenter
+ y: 120
+ MouseArea {
+ id: buttonMouseArea
+ objectName: "buttonMouseArea"
+ anchors.fill: parent
+ onClicked: {
+ // once the "console" context has been declared,
+ // slots can be called like functions
+ pyConsole.outputFloat(123)
+ pyConsole.outputStr("foobar")
+ pyConsole.output(helloText.x)
+ pyConsole.output(helloText.text)
+ }
+ }
+ Text {
+ id: buttonText
+ text: "Press me!"
+ anchors.horizontalCenter: button.horizontalCenter
+ anchors.verticalCenter: button.verticalCenter
+ font.pointSize: 16
+ }
+ }
+}
diff --git a/examples/qml/signals/qmltopy2/main.py b/examples/qml/signals/qmltopy2/main.py
new file mode 100644
index 000000000..bc2e8d3ed
--- /dev/null
+++ b/examples/qml/signals/qmltopy2/main.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from pathlib import Path
+import sys
+from PySide6.QtCore import QObject, QUrl, Slot
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+from PySide6.QtQml import QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "examples.signals.qmltopy2"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class RotateValue(QObject):
+ def __init__(self):
+ super().__init__()
+ self.r = 0
+
+ # If a slot returns a value, the return value type must be explicitly
+ # defined in the decorator.
+ @Slot(result=int)
+ def val(self):
+ self.r = self.r + 10
+ return self.r
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.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)
diff --git a/examples/qml/signals/qmltopy2/qmltopy2.pyproject b/examples/qml/signals/qmltopy2/qmltopy2.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/signals/qmltopy2/qmltopy2.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/signals/qmltopy2/view.qml b/examples/qml/signals/qmltopy2/view.qml
new file mode 100644
index 000000000..4bbda574c
--- /dev/null
+++ b/examples/qml/signals/qmltopy2/view.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+import examples.signals.qmltopy2 1.0
+
+Rectangle {
+ id: page
+
+ width: 500; height: 200
+ color: "lightgray"
+
+ RotateValue {
+ id: rotatevalue
+ }
+
+ Text {
+ id: helloText
+ text: "Hello world!"
+ anchors.horizontalCenter: page.horizontalCenter
+ y: 30
+ font.pointSize: 24; font.bold: true
+ }
+
+
+ Rectangle {
+ id: button
+ width: 150; height: 40
+ color: "darkgray"
+ anchors.horizontalCenter: page.horizontalCenter
+ y: 120
+ MouseArea {
+ id: buttonMouseArea
+ objectName: "buttonMouseArea"
+ anchors.fill: parent
+ onClicked: {
+ helloText.rotation = rotatevalue.val()
+ }
+ }
+ Text {
+ id: buttonText
+ text: "Press me!"
+ anchors.horizontalCenter: button.horizontalCenter
+ anchors.verticalCenter: button.verticalCenter
+ font.pointSize: 16
+ }
+ }
+}
diff --git a/examples/qml/signals/qmltopy3/main.py b/examples/qml/signals/qmltopy3/main.py
new file mode 100644
index 000000000..6a5554842
--- /dev/null
+++ b/examples/qml/signals/qmltopy3/main.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from pathlib import Path
+import sys
+from PySide6.QtCore import QUrl
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+
+def sayThis(s):
+ print(s)
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
+ view.setSource(QUrl.fromLocalFile(qml_file))
+ if view.status() == QQuickView.Error:
+ sys.exit(-1)
+
+ root = view.rootObject()
+ root.textRotationChanged.connect(sayThis)
+ root.buttonClicked.connect(lambda: sayThis("clicked button (QML top-level signal)"))
+
+ 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)
diff --git a/examples/qml/signals/qmltopy3/qmltopy3.pyproject b/examples/qml/signals/qmltopy3/qmltopy3.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/signals/qmltopy3/qmltopy3.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/signals/qmltopy3/view.qml b/examples/qml/signals/qmltopy3/view.qml
new file mode 100644
index 000000000..3e8a0f564
--- /dev/null
+++ b/examples/qml/signals/qmltopy3/view.qml
@@ -0,0 +1,66 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: page
+
+ signal buttonClicked
+ signal textRotationChanged(double rot)
+
+ width: 500; height: 200
+ color: "lightgray"
+
+ Text {
+ id: helloText
+ text: "Hello world!"
+ y: 30
+ x: page.width/2-width/2
+ font.pointSize: 24; font.bold: true
+ onRotationChanged: textRotationChanged(rotation)
+
+ states: State {
+ name: "down"; when: buttonMouseArea.pressed === true
+ PropertyChanges {
+ target: helloText;
+ rotation: 180;
+ y: 100;
+ }
+ }
+
+ transitions: Transition {
+ from: ""; to: "down"; reversible: true
+ ParallelAnimation {
+ NumberAnimation {
+ properties: "y,rotation"
+ duration: 500
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: button
+ width: 150; height: 40
+ color: "darkgray"
+ anchors.horizontalCenter: page.horizontalCenter
+ y: 120
+ MouseArea {
+ id: buttonMouseArea
+ objectName: "buttonMouseArea"
+ anchors.fill: parent
+ onClicked: {
+ buttonClicked()
+ }
+ }
+ Text {
+ id: buttonText
+ text: "Press me!"
+ anchors.horizontalCenter: button.horizontalCenter
+ anchors.verticalCenter: button.verticalCenter
+ font.pointSize: 16
+ }
+ }
+}
diff --git a/examples/qml/signals/qmltopy4/main.py b/examples/qml/signals/qmltopy4/main.py
new file mode 100644
index 000000000..8a56073d1
--- /dev/null
+++ b/examples/qml/signals/qmltopy4/main.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from pathlib import Path
+import sys
+from PySide6.QtCore import QObject, QUrl
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+
+def sayThis(s):
+ print(s)
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
+ view.setSource(QUrl.fromLocalFile(qml_file))
+ if view.status() == QQuickView.Error:
+ sys.exit(-1)
+
+ root = view.rootObject()
+ button = root.findChild(QObject, "buttonMouseArea")
+ button.clicked.connect(lambda: sayThis("clicked button (signal directly connected)"))
+
+ 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)
diff --git a/examples/qml/signals/qmltopy4/qmltopy4.pyproject b/examples/qml/signals/qmltopy4/qmltopy4.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/signals/qmltopy4/qmltopy4.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/signals/qmltopy4/view.qml b/examples/qml/signals/qmltopy4/view.qml
new file mode 100644
index 000000000..771b14c47
--- /dev/null
+++ b/examples/qml/signals/qmltopy4/view.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: page
+
+ width: 500; height: 200
+ color: "lightgray"
+
+ Rectangle {
+ id: button
+ width: 150; height: 40
+ color: "darkgray"
+ anchors.horizontalCenter: page.horizontalCenter
+ anchors.verticalCenter: page.verticalCenter
+ MouseArea {
+ id: buttonMouseArea
+ objectName: "buttonMouseArea"
+ anchors.fill: parent
+ }
+ Text {
+ id: buttonText
+ text: "Press me!"
+ anchors.horizontalCenter: button.horizontalCenter
+ anchors.verticalCenter: button.verticalCenter
+ font.pointSize: 16
+ }
+ }
+}
diff --git a/examples/qml/textproperties/doc/textproperties.png b/examples/qml/textproperties/doc/textproperties.png
new file mode 100644
index 000000000..b14bb73d7
--- /dev/null
+++ b/examples/qml/textproperties/doc/textproperties.png
Binary files differ
diff --git a/examples/qml/textproperties/doc/textproperties.rst b/examples/qml/textproperties/doc/textproperties.rst
new file mode 100644
index 000000000..81829dfdc
--- /dev/null
+++ b/examples/qml/textproperties/doc/textproperties.rst
@@ -0,0 +1,9 @@
+Text Properties Example
+=======================
+
+A Python application that demonstrates how to load a qml file
+using Material design, to change the look of text.
+
+.. image:: textproperties.png
+ :width: 400
+ :alt: Text Properties Screenshot
diff --git a/examples/qml/textproperties/main.py b/examples/qml/textproperties/main.py
new file mode 100644
index 000000000..4e6afc9ff
--- /dev/null
+++ b/examples/qml/textproperties/main.py
@@ -0,0 +1,76 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtCore import QObject, Slot
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine, QmlElement
+from PySide6.QtQuickControls2 import QQuickStyle
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "io.qt.textproperties"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class Bridge(QObject):
+
+ @Slot(str, result=str)
+ def getColor(self, s):
+ if s.lower() == "red":
+ return "#ef9a9a"
+ elif s.lower() == "green":
+ return "#a5d6a7"
+ elif s.lower() == "blue":
+ return "#90caf9"
+ else:
+ return "white"
+
+ @Slot(float, result=int)
+ def getSize(self, s):
+ size = int(s * 34)
+ if size <= 0:
+ return 1
+ else:
+ return size
+
+ @Slot(str, result=bool)
+ def getItalic(self, s):
+ if s.lower() == "italic":
+ return True
+ else:
+ return False
+
+ @Slot(str, result=bool)
+ def getBold(self, s):
+ if s.lower() == "bold":
+ return True
+ else:
+ return False
+
+ @Slot(str, result=bool)
+ def getUnderline(self, s):
+ if s.lower() == "underline":
+ return True
+ else:
+ return False
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ QQuickStyle.setStyle("Material")
+ engine = QQmlApplicationEngine()
+
+ # Get the path of the current directory, and then add the name
+ # of the QML file, to load it.
+ qml_file = Path(__file__).parent / 'view.qml'
+ engine.load(qml_file)
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ sys.exit(app.exec())
diff --git a/examples/qml/textproperties/textproperties.pyproject b/examples/qml/textproperties/textproperties.pyproject
new file mode 100644
index 000000000..e6f087cce
--- /dev/null
+++ b/examples/qml/textproperties/textproperties.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "view.qml"]
+}
diff --git a/examples/qml/textproperties/view.qml b/examples/qml/textproperties/view.qml
new file mode 100644
index 000000000..c722eb4eb
--- /dev/null
+++ b/examples/qml/textproperties/view.qml
@@ -0,0 +1,160 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Window
+import QtQuick.Controls.Material
+
+import io.qt.textproperties
+
+ApplicationWindow {
+ id: page
+ width: 800
+ height: 400
+ visible: true
+ Material.theme: Material.Dark
+ Material.accent: Material.Red
+
+ Bridge {
+ id: bridge
+ }
+
+ GridLayout {
+ id: grid
+ columns: 2
+ rows: 3
+
+ ColumnLayout {
+ spacing: 2
+ Layout.columnSpan: 1
+ Layout.preferredWidth: 400
+
+ Text {
+ id: leftlabel
+ Layout.alignment: Qt.AlignHCenter
+ color: "white"
+ font.pointSize: 16
+ text: "Qt for Python"
+ Layout.preferredHeight: 100
+ Material.accent: Material.Green
+ }
+
+ RadioButton {
+ id: italic
+ Layout.alignment: Qt.AlignLeft
+ text: "Italic"
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(italic.text)
+ leftlabel.font.bold = bridge.getBold(italic.text)
+ leftlabel.font.underline = bridge.getUnderline(italic.text)
+
+ }
+ }
+ RadioButton {
+ id: bold
+ Layout.alignment: Qt.AlignLeft
+ text: "Bold"
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(bold.text)
+ leftlabel.font.bold = bridge.getBold(bold.text)
+ leftlabel.font.underline = bridge.getUnderline(bold.text)
+ }
+ }
+ RadioButton {
+ id: underline
+ Layout.alignment: Qt.AlignLeft
+ text: "Underline"
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(underline.text)
+ leftlabel.font.bold = bridge.getBold(underline.text)
+ leftlabel.font.underline = bridge.getUnderline(underline.text)
+ }
+ }
+ RadioButton {
+ id: noneradio
+ Layout.alignment: Qt.AlignLeft
+ text: "None"
+ checked: true
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(noneradio.text)
+ leftlabel.font.bold = bridge.getBold(noneradio.text)
+ leftlabel.font.underline = bridge.getUnderline(noneradio.text)
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: rightcolumn
+ spacing: 2
+ Layout.columnSpan: 1
+ Layout.preferredWidth: 400
+ Layout.preferredHeight: 400
+ Layout.fillWidth: true
+
+ RowLayout {
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+
+
+ Button {
+ id: red
+ text: "Red"
+ highlighted: true
+ Material.accent: Material.Red
+ onClicked: {
+ leftlabel.color = bridge.getColor(red.text)
+ }
+ }
+ Button {
+ id: green
+ text: "Green"
+ highlighted: true
+ Material.accent: Material.Green
+ onClicked: {
+ leftlabel.color = bridge.getColor(green.text)
+ }
+ }
+ Button {
+ id: blue
+ text: "Blue"
+ highlighted: true
+ Material.accent: Material.Blue
+ onClicked: {
+ leftlabel.color = bridge.getColor(blue.text)
+ }
+ }
+ Button {
+ id: nonebutton
+ text: "None"
+ highlighted: true
+ Material.accent: Material.BlueGrey
+ onClicked: {
+ leftlabel.color = bridge.getColor(nonebutton.text)
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ Text {
+ id: rightlabel
+ color: "white"
+ Layout.alignment: Qt.AlignLeft
+ text: "Font size"
+ Material.accent: Material.White
+ }
+ Slider {
+ width: rightcolumn.width*0.6
+ Layout.alignment: Qt.AlignRight
+ id: slider
+ value: 0.5
+ onValueChanged: {
+ leftlabel.font.pointSize = bridge.getSize(value)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/adding/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/adding/People/Main.qml
new file mode 100644
index 000000000..8d963a861
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/adding/People/Main.qml
@@ -0,0 +1,9 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+Person {
+ name: "Bob Jones"
+ shoe_size: 12
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/adding/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/adding/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/adding/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/adding/adding.pyproject b/examples/qml/tutorials/extending-qml-advanced/adding/adding.pyproject
new file mode 100644
index 000000000..3219f79ca
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/adding/adding.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/adding/doc/adding.rst b/examples/qml/tutorials/extending-qml-advanced/adding/doc/adding.rst
new file mode 100644
index 000000000..4c1b3bdae
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/adding/doc/adding.rst
@@ -0,0 +1,67 @@
+.. _qml-adding-types-example:
+
+Extending QML - Adding Types Example
+====================================
+
+The Adding Types Example shows how to add a new object type, ``Person``, to QML.
+The ``Person`` type can be used from QML like this:
+
+.. code-block:: javascript
+
+ import examples.adding.people
+
+ Person {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+
+Declare the Person Class
+------------------------
+
+All QML types map to C++ types. Here we declare a basic C++ Person class
+with the two properties we want accessible on the QML type - name and shoeSize.
+Although in this example we use the same name for the C++ class as the QML
+type, the C++ class can be named differently, or appear in a namespace.
+
+The Person class implementation is quite basic. The property accessors simply
+return members of the object instance.
+
+.. code-block:: python
+
+ from PySide6.QtCore import QObject, Property
+ from PySide6.QtQml import QmlElement
+
+ # To be used on the @QmlElement decorator
+ # (QML_IMPORT_MINOR_VERSION is optional)
+ QML_IMPORT_NAME = "People"
+ QML_IMPORT_MAJOR_VERSION = 1
+
+
+ @QmlElement
+ class Person(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ self._name = n
+
+ @Property(int)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
+
+Running the Example
+-------------------
+
+The main.py file in the example includes a simple shell application that
+loads and runs the QML snippet shown at the beginning of this page.
diff --git a/examples/qml/tutorials/extending-qml-advanced/adding/main.py b/examples/qml/tutorials/extending-qml-advanced/adding/main.py
new file mode 100644
index 000000000..ec703dbf3
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/adding/main.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the qml/examples/qml/referenceexamples/adding example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Person # noqa: F401
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ engine = QQmlEngine()
+ engine.addImportPath(Path(__file__).parent)
+ component = QQmlComponent(engine)
+ component.loadFromModule("People", "Main")
+
+ person = component.create()
+ if person:
+ print(f"The person's name is {person.name}")
+ print(f"They wear a {person.shoe_size} sized shoe")
+ else:
+ print(component.errors())
+ del engine
+ sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/adding/person.py b/examples/qml/tutorials/extending-qml-advanced/adding/person.py
new file mode 100644
index 000000000..526eae714
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/adding/person.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property
+from PySide6.QtQml import QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class Person(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ self._name = n
+
+ @Property(int)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/Main.qml
new file mode 100644
index 000000000..c14051371
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/Main.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Person {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+ guests: [
+ Person { name: "Leo Hodges" },
+ Person { name: "Jack Smith" },
+ Person { name: "Anne Brown" }
+ ]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/advanced1-Base-project.pyproject b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/advanced1-Base-project.pyproject
new file mode 100644
index 000000000..09942ebcc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/advanced1-Base-project.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py
new file mode 100644
index 000000000..764815175
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class BirthdayParty(QObject):
+ host_changed = Signal()
+ guests_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person, notify=host_changed, final=True)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+ self.guests_changed.emit()
+
+ guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/doc/advanced1-Base-project.rst b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/doc/advanced1-Base-project.rst
new file mode 100644
index 000000000..90a73b78d
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/doc/advanced1-Base-project.rst
@@ -0,0 +1,57 @@
+.. _qml-advanced1-base-project:
+
+Extending QML (advanced) - BirthdayParty Base Project
+=====================================================
+
+This is the first of a series of 6 examples forming a tutorial using the
+example of a birthday party to demonstrate some of the advanced features of
+QML. The code for the various features explained below is based on this
+birthday party project and relies on some of the material in the basic
+tutorial. This simple example is then expanded upon to illustrate the various
+QML extensions explained below. The complete code for each new extension to the
+code can be found at the end of the respective page.
+
+The base project defines the ``Person`` class and the ``BirthdayParty`` class,
+which model the attendees and the party itself respectively.
+
+.. literalinclude:: person.py
+ :lineno-start: 13
+ :lines: 13-41
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 16
+ :lines: 16-46
+
+All the information about the party can then be stored in the corresponding QML
+file.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 4
+ :lines: 4-16
+
+
+The ``main.py`` file creates a simple shell application that displays whose
+birthday it is and who is invited to their party.
+
+.. literalinclude:: main.py
+ :lineno-start: 17
+ :lines: 17-21
+
+The app outputs the following summary of the party::
+
+ "Bob Jones" is having a birthday!
+ They are inviting:
+ "Leo Hodges"
+ "Jack Smith"
+ "Anne Brown"
+
+Outlook
+-------
+
+The following sections go into how to add support for ``Boy`` and ``Girl``
+attendees instead of just ``Person`` by using inheritance and coercion, how to
+make use of default properties to implicitly assign attendees of the party as
+guests, how to assign properties as groups instead of one by one, how to use
+attached objects to keep track of invited guests' reponses, how to use a
+property value source to display the lyrics of the happy birthday song over
+time, and how to expose third party objects to QML.
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py
new file mode 100644
index 000000000..560db6602
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Person # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+app = QCoreApplication(sys.argv)
+engine = QQmlEngine()
+engine.addImportPath(Path(__file__).parent)
+component = QQmlComponent(engine)
+component.loadFromModule("People", "Main")
+party = component.create()
+if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+host = party.host
+print(f"{host.name} is having a birthday!\nThey are inviting:")
+for g in range(party.guestCount()):
+ name = party.guest(g).name
+ print(f" {name}")
+del engine
+sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.py b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.py
new file mode 100644
index 000000000..60dc9d882
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class Person(QObject):
+ name_changed = Signal()
+ shoe_size_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(int, notify=shoe_size_changed, final=True)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ if self._shoe_size != s:
+ self._shoe_size = s
+ self.shoe_size_changed.emit()
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/Main.qml
new file mode 100644
index 000000000..b2b7ace93
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/Main.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Boy {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+ guests: [
+ Boy { name: "Leo Hodges" },
+ Boy { name: "Jack Smith" },
+ Girl { name: "Anne Brown" }
+ ]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/advanced2-Inheritance-and-coercion.pyproject b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/advanced2-Inheritance-and-coercion.pyproject
new file mode 100644
index 000000000..09942ebcc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/advanced2-Inheritance-and-coercion.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.py
new file mode 100644
index 000000000..764815175
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class BirthdayParty(QObject):
+ host_changed = Signal()
+ guests_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person, notify=host_changed, final=True)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+ self.guests_changed.emit()
+
+ guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/doc/advanced2-Inheritance-and-coercion.rst b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/doc/advanced2-Inheritance-and-coercion.rst
new file mode 100644
index 000000000..16c0dbc9a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/doc/advanced2-Inheritance-and-coercion.rst
@@ -0,0 +1,62 @@
+.. _qml-advanced2-inheritance-and-coercion:
+
+Extending QML (advanced) - Inheritance and Coercion
+===================================================
+
+This is the second of a series of 6 examples forming a tutorial using the
+example of a birthday party to demonstrate some of the advanced features of
+QML.
+
+Right now, each attendant is being modelled as a person. This is a bit too
+generic and it would be nice to be able to know more about the attendees. By
+specializing them as boys and girls, we can already get a better idea of who's
+coming.
+
+To do this, the ``Boy`` and ``Girl`` classes are introduced, both inheriting from
+``Person``.
+
+.. literalinclude:: person.py
+ :lineno-start: 43
+ :lines: 43-46
+
+.. literalinclude:: person.py
+ :lineno-start: 49
+ :lines: 49-52
+
+The ``Person`` class remains unaltered and the ``Boy`` and ``Girl`` classes are
+trivial extensions of it. The types and their QML name are registered with the
+QML engine with ``@QmlElement``.
+
+Notice that the ``host`` and ``guests`` properties in ``BirthdayParty`` still
+take instances of ``Person``.
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 26
+ :lines: 26-26
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 46
+ :lines: 46-46
+
+The implementation of the ``Person`` class itself has not been changed.
+However, as the ``Person`` class was repurposed as a common base for ``Boy``
+and ``Girl``, ``Person`` should no longer be instantiable from QML directly. An
+explicit ``Boy`` or ``Girl`` should be instantiated instead.
+
+.. literalinclude:: person.py
+ :lineno-start: 13
+ :lines: 13-15
+
+While we want to disallow instantiating ``Person`` from within QML, it still
+needs to be registered with the QML engine so that it can be used as a property
+type and other types can be coerced to it. This is what the ``@QmlUncreatable``
+macro does. As all three types, ``Person``, ``Boy`` and ``Girl``, have been
+registered with the QML system, on assignment, QML automatically (and
+type-safely) converts the ``Boy`` and ``Girl`` objects into a ``Person``.
+
+With these changes in place, we can now specify the birthday party with the
+extra information about the attendees as follows.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 6
+ :lines: 6-16
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py
new file mode 100644
index 000000000..cc77e2b40
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example
+ from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+app = QCoreApplication(sys.argv)
+engine = QQmlEngine()
+engine.addImportPath(Path(__file__).parent)
+component = QQmlComponent(engine)
+component.loadFromModule("People", "Main")
+party = component.create()
+if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+host = party.host
+print(f"{host.name} is having a birthday!")
+if isinstance(host, Boy):
+ print("He is inviting:")
+else:
+ print("She is inviting:")
+for g in range(party.guestCount()):
+ name = party.guest(g).name
+ print(f" {name}")
+del engine
+sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.py b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.py
new file mode 100644
index 000000000..57e73e6f5
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlElement, QmlUncreatable
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+@QmlUncreatable("Person is an abstract base class.")
+class Person(QObject):
+ name_changed = Signal()
+ shoe_size_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(int, notify=shoe_size_changed, final=True)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
+
+
+@QmlElement
+class Boy(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+@QmlElement
+class Girl(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/Main.qml
new file mode 100644
index 000000000..9971a2315
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/Main.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Boy {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+
+ Boy { name: "Leo Hodges" }
+ Boy { name: "Jack Smith" }
+ Girl { name: "Anne Brown" }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/advanced3-Default-properties.pyproject b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/advanced3-Default-properties.pyproject
new file mode 100644
index 000000000..09942ebcc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/advanced3-Default-properties.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.py
new file mode 100644
index 000000000..3f6102c66
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, ClassInfo, Property, Signal
+from PySide6.QtQml import QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+@ClassInfo(DefaultProperty="guests")
+class BirthdayParty(QObject):
+ host_changed = Signal()
+ guests_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person, notify=host_changed, final=True)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+ self.guests_changed.emit()
+
+ guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/doc/advanced3-Default-properties.rst b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/doc/advanced3-Default-properties.rst
new file mode 100644
index 000000000..0857f9d0a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/doc/advanced3-Default-properties.rst
@@ -0,0 +1,40 @@
+.. _qml-advanced3-default-properties:
+
+Extending QML (advanced) - Default Properties
+=============================================
+
+This is the third of a series of 6 examples forming a tutorial using the
+example of a birthday party to demonstrate some of the advanced features of
+QML.
+
+Currently, in the QML file, each property is assigned explicitly. For example,
+the ``host`` property is assigned a ``Boy`` and the ``guests`` property is
+assigned a list of ``Boy`` or ``Girl``. This is easy but it can be made a bit
+simpler for this specific use case. Instead of assigning the ``guests``
+property explicitly, we can add ``Boy`` and ``Girl`` objects inside the party
+directly and have them assigned to ``guests`` implicitly. It makes sense that
+all the attendees that we specify, and that are not the host, are guests. This
+change is purely syntactical but it can add a more natural feel in many
+situations.
+
+The ``guests`` property can be designated as the default property of
+``BirthdayParty``. Meaning that each object created inside of a
+``BirthdayParty`` is implicitly appended to the default property ``guests``.
+The resulting QML looks like this.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 6
+ :lines: 6-15
+
+The only change required to enable this behavior is to add the ``DefaultProperty``
+class info annotation to ``BirthdayParty`` to designate ``guests`` as its default
+property.
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 16
+ :lines: 16-18
+
+You may already be familiar with this mechanism. The default property for all
+descendants of ``Item`` in QML is the ``data`` property. All elements not
+explicitly added to a property of an ``Item`` will be added to ``data``. This
+makes the structure clear and reduces unnecessary noise in the code.
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py
new file mode 100644
index 000000000..020974c9b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example
+ from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+app = QCoreApplication(sys.argv)
+engine = QQmlEngine()
+engine.addImportPath(Path(__file__).parent)
+component = QQmlComponent(engine)
+component.loadFromModule("People", "Main")
+party = component.create()
+if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+host = party.host
+print(f"{host.name} is having a birthday!")
+if isinstance(host, Boy):
+ print("He is inviting:")
+else:
+ print("She is inviting:")
+for g in range(party.guestCount()):
+ name = party.guest(g).name
+ print(f" {name}")
+del engine
+sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.py b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.py
new file mode 100644
index 000000000..503aaf65e
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlAnonymous, QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class Person(QObject):
+ name_changed = Signal()
+ shoe_size_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(int, notify=shoe_size_changed, final=True)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
+
+
+@QmlElement
+class Boy(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+@QmlElement
+class Girl(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/Main.qml
new file mode 100644
index 000000000..3c34234fd
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/Main.qml
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+import People
+
+BirthdayParty {
+ host: Boy {
+ name: "Bob Jones"
+ shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 }
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ shoe { size: 10; color: "black"; brand: "Thebok"; price: 59.95 }
+ }
+ Boy { name: "Jack Smith"
+ shoe {
+ size: 8
+ color: "blue"
+ brand: "Luma"
+ price: 19.95
+ }
+ }
+ Girl {
+ name: "Anne Brown"
+ shoe.size: 7
+ shoe.color: "red"
+ shoe.brand: "Job Macobs"
+ shoe.price: 699.99
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/advanced4-Grouped-properties.pyproject b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/advanced4-Grouped-properties.pyproject
new file mode 100644
index 000000000..09942ebcc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/advanced4-Grouped-properties.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.py
new file mode 100644
index 000000000..3f6102c66
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, ClassInfo, Property, Signal
+from PySide6.QtQml import QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+@ClassInfo(DefaultProperty="guests")
+class BirthdayParty(QObject):
+ host_changed = Signal()
+ guests_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person, notify=host_changed, final=True)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+ self.guests_changed.emit()
+
+ guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/doc/advanced4-Grouped-properties.rst b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/doc/advanced4-Grouped-properties.rst
new file mode 100644
index 000000000..7748d3189
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/doc/advanced4-Grouped-properties.rst
@@ -0,0 +1,39 @@
+.. _qml-advanced-advanced4-grouped-properties:
+
+Extending QML (advanced) - Grouped Properties
+=============================================
+
+This is the fourth of a series of 6 examples forming a tutorial using the
+example of a birthday party to demonstrate some of the advanced features of
+QML.
+
+More information is needed about the shoes of the guests. Aside from their
+size, we also want to store the shoes' color, brand, and price. This
+information is stored in a ``ShoeDescription`` class.
+
+.. literalinclude:: person.py
+ :lineno-start: 14
+ :lines: 14-66
+
+Each person now has two properties, a ``name`` and a shoe description ``shoe``.
+
+.. literalinclude:: person.py
+ :lineno-start: 69
+ :lines: 69-90
+
+Specifying the values for each element of the shoe description works but is a
+bit repetitive.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 26
+ :lines: 26-32
+
+Grouped properties provide a more elegant way of assigning these properties.
+Instead of assigning the values to each property one-by-one, the individual
+values can be passed as a group to the ``shoe`` property making the code more
+readable. No changes are required to enable this feature as it is available by
+default for all of QML.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 9
+ :lines: 9-12
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py
new file mode 100644
index 000000000..9757b6daa
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example
+ from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+ engine = QQmlEngine()
+ engine.addImportPath(Path(__file__).parent)
+ component = QQmlComponent(engine)
+ component.loadFromModule("People", "Main")
+
+ party = component.create()
+ if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+ host = party.host
+ print(f"{host.name} is having a birthday!")
+ if isinstance(host, Boy):
+ print("He is inviting:")
+ else:
+ print("She is inviting:")
+ best_shoe = None
+ for g in range(party.guestCount()):
+ guest = party.guest(g)
+ name = guest.name
+ print(f" {name}")
+ if not best_shoe or best_shoe.shoe.price < guest.shoe.price:
+ best_shoe = guest
+ if best_shoe:
+ print(f"{best_shoe.name} is wearing the best shoes!")
+ del engine
+ sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.py b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.py
new file mode 100644
index 000000000..ccd439e88
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtGui import QColor
+from PySide6.QtQml import QmlAnonymous, QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class ShoeDescription(QObject):
+ brand_changed = Signal()
+ size_changed = Signal()
+ price_changed = Signal()
+ color_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._brand = ''
+ self._size = 0
+ self._price = 0
+ self._color = QColor()
+
+ @Property(str, notify=brand_changed, final=True)
+ def brand(self):
+ return self._brand
+
+ @brand.setter
+ def brand(self, b):
+ if self._brand != b:
+ self._brand = b
+ self.brand_changed.emit()
+
+ @Property(int, notify=size_changed, final=True)
+ def size(self):
+ return self._size
+
+ @size.setter
+ def size(self, s):
+ if self._size != s:
+ self._size = s
+ self.size_changed.emit()
+
+ @Property(float, notify=price_changed, final=True)
+ def price(self):
+ return self._price
+
+ @price.setter
+ def price(self, p):
+ if self._price != p:
+ self._price = p
+ self.price_changed.emit()
+
+ @Property(QColor, notify=color_changed, final=True)
+ def color(self):
+ return self._color
+
+ @color.setter
+ def color(self, c):
+ if self._color != c:
+ self._color = c
+ self.color_changed.emit()
+
+
+@QmlAnonymous
+class Person(QObject):
+ name_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe = ShoeDescription()
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(ShoeDescription, final=True)
+ def shoe(self):
+ return self._shoe
+
+
+@QmlElement
+class Boy(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+@QmlElement
+class Girl(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/Main.qml
new file mode 100644
index 000000000..795d63867
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/Main.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ Boy {
+ name: "Robert Campbell"
+ BirthdayParty.rsvp: "2009-07-01"
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ shoe_size: 10
+ BirthdayParty.rsvp: "2009-07-06"
+ }
+
+ host: Boy {
+ name: "Jack Smith"
+ shoe_size: 8
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/advanced5-Attached-properties.pyproject b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/advanced5-Attached-properties.pyproject
new file mode 100644
index 000000000..09942ebcc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/advanced5-Attached-properties.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.py
new file mode 100644
index 000000000..f38bfd305
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QDate, QObject, ClassInfo, Property, Signal
+from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class BirthdayPartyAttached(QObject):
+ rsvp_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._rsvp = QDate()
+
+ @Property(QDate, notify=rsvp_changed, final=True)
+ def rsvp(self):
+ return self._rsvp
+
+ @rsvp.setter
+ def rsvp(self, d):
+ if self._rsvp != d:
+ self._rsvp = d
+ self.rsvp_changed.emit()
+
+
+@QmlElement
+@ClassInfo(DefaultProperty="guests")
+@QmlAttached(BirthdayPartyAttached)
+class BirthdayParty(QObject):
+ host_changed = Signal()
+ guests_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person, notify=host_changed, final=True)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+ self.guests_changed.emit()
+
+ @staticmethod
+ def qmlAttachedProperties(self, o):
+ return BirthdayPartyAttached(o)
+
+ guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/doc/advanced5-Attached-properties.rst b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/doc/advanced5-Attached-properties.rst
new file mode 100644
index 000000000..14b4bddb0
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/doc/advanced5-Attached-properties.rst
@@ -0,0 +1,51 @@
+.. _qml-advanced-advanced5-attached-properties:
+
+Extending QML (advanced) - Attached Properties
+==============================================
+
+This is the fifth of a series of 6 examples forming a tutorial using the
+example of a birthday party to demonstrate some of the advanced features of
+QML.
+
+The time has come for the host to send out invitations. To keep track of which
+guests have responded to the invitation and when, we need somewhere to store
+that information. Storing it in the ``BirthdayParty`` object iself would not
+really fit. A better way would be to store the responses as attached objects to
+the party object.
+
+First, we declare the ``BirthdayPartyAttached`` class which holds the guest reponses.
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 16
+ :lines: 16-32
+
+And we attach it to the ``BirthdayParty`` class and define
+``qmlAttachedProperties()`` to return the attached object.
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 34
+ :lines: 34-38
+
+.. literalinclude:: birthdayparty.py
+ :lineno-start: 67
+ :lines: 67-69
+
+Now, attached objects can be used in the QML to hold the rsvp information of
+the invited guests.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 6
+ :lines: 6-22
+
+Finally, the information can be accessed in the following way.
+
+.. literalinclude:: main.py
+ :lineno-start: 36
+ :lines: 36-39
+
+The program outputs the following summary of the party to come::
+
+ "Jack Smith" is having a birthday!
+ He is inviting:
+ "Robert Campbell" RSVP date: "Wed Mar 1 2023"
+ "Leo Hodges" RSVP date: "Mon Mar 6 2023"
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py
new file mode 100644
index 000000000..9a92afeb5
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py
@@ -0,0 +1,48 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example
+ from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
+
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+app = QCoreApplication(sys.argv)
+engine = QQmlEngine()
+engine.addImportPath(Path(__file__).parent)
+component = QQmlComponent(engine)
+component.loadFromModule("People", "Main")
+party = component.create()
+if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+host = party.host
+print(f"{host.name} is having a birthday!")
+if isinstance(host, Boy):
+ print("He is inviting:")
+else:
+ print("She is inviting:")
+for g in range(party.guestCount()):
+ guest = party.guest(g)
+ name = guest.name
+
+ rsvp_date = None
+ attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False)
+ if attached:
+ rsvp_date = attached.rsvp.toString()
+ if rsvp_date:
+ print(f" {name} RSVP date: {rsvp_date}")
+ else:
+ print(f" {name} RSVP date: Hasn't RSVP'd")
+
+del engine
+sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.py b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.py
new file mode 100644
index 000000000..503aaf65e
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlAnonymous, QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class Person(QObject):
+ name_changed = Signal()
+ shoe_size_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(int, notify=shoe_size_changed, final=True)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
+
+
+@QmlElement
+class Boy(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+@QmlElement
+class Girl(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/Main.qml
new file mode 100644
index 000000000..254265a80
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/Main.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ HappyBirthdaySong on announcement { name: "Bob Jones" }
+
+ onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
+
+ host: Boy {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ BirthdayParty.rsvp: "2009-07-06"
+ }
+ Boy {
+ name: "Jack Smith"
+ }
+ Girl {
+ name: "Anne Brown"
+ BirthdayParty.rsvp: "2009-07-01"
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/advanced6-Property-value-source.pyproject b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/advanced6-Property-value-source.pyproject
new file mode 100644
index 000000000..fe2980fa9
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/advanced6-Property-value-source.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "happybirthdaysong.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.py
new file mode 100644
index 000000000..eacb5201d
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.py
@@ -0,0 +1,89 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QDate, QObject, ClassInfo, Property, QTime, Signal
+from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class BirthdayPartyAttached(QObject):
+ rsvp_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._rsvp = QDate()
+
+ @Property(QDate, notify=rsvp_changed, final=True)
+ def rsvp(self):
+ return self._rsvp
+
+ @rsvp.setter
+ def rsvp(self, d):
+ if self._rsvp != d:
+ self._rsvp = d
+ self.rsvp_changed.emit()
+
+
+@QmlElement
+@ClassInfo(DefaultProperty="guests")
+@QmlAttached(BirthdayPartyAttached)
+class BirthdayParty(QObject):
+
+ announcement_changed = Signal()
+ host_changed = Signal()
+ guests_changed = Signal()
+ partyStarted = Signal(QTime)
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._announcement = ""
+ self._host = None
+ self._guests = []
+
+ def startParty(self):
+ self.partyStarted.emit(QTime.currentTime())
+
+ @Property(Person, notify=host_changed, final=True)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ @Property(str, notify=announcement_changed, final=True)
+ def announcement(self):
+ return self._announcement
+
+ @announcement.setter
+ def announcement(self, a):
+ if self._announcement != a:
+ self._announcement = a
+ self.announcement_changed.emit()
+ print(a)
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+ self.guests_changed.emit()
+
+ @staticmethod
+ def qmlAttachedProperties(self, o):
+ return BirthdayPartyAttached(o)
+
+ guests = ListProperty(Person, appendGuest, notify=guests_changed, final=True)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/doc/advanced6-Property-value-source.rst b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/doc/advanced6-Property-value-source.rst
new file mode 100644
index 000000000..4e1dc393a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/doc/advanced6-Property-value-source.rst
@@ -0,0 +1,43 @@
+.. _qml-advanced/advanced6-property-value-source:
+
+Extending QML (advanced) - Property Value Source
+================================================
+
+This is the last of a series of 6 examples forming a tutorial using the example
+of a birthday party to demonstrate some of the advanced features of QML.
+
+During the party the guests have to sing for the host. It would be handy if the
+program could display the lyrics customized for the occasion to help the
+guests. To this end, a property value source is used to generate the verses of
+the song over time.
+
+.. literalinclude:: happybirthdaysong.py
+ :lineno-start: 13
+ :lines: 13-49
+
+The class ``HappyBirthdaySong`` is added as a value source. It must inherit
+from ``QQmlPropertyValueSource`` and implement its interface. The
+``setTarget()`` function is used to define which property this source acts
+upon. In this case, the value source writes to the ``announcement`` property of
+the ``BirthdayParty`` to display the lyrics over time. It has an internal timer
+that causes the ``announcement`` property of the party to be set to the next
+line of the lyrics repeatedly.
+
+In QML, a ``HappyBirthdaySong`` is instantiated inside the ``BirthdayParty``.
+The ``on`` keyword in its signature is used to specify the property that the
+value source targets, in this case ``announcement``. The ``name`` property of
+the ``HappyBirthdaySong`` object is also bound to the name of the host of the
+party.
+
+.. literalinclude:: People/Main.qml
+ :lineno-start: 6
+ :lines: 6-7
+
+The program displays the time at which the party started using the
+``partyStarted`` signal and then prints the following happy birthday verses
+over and over::
+
+ Happy birthday to you,
+ Happy birthday to you,
+ Happy birthday dear Bob Jones,
+ Happy birthday to you!
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.py b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.py
new file mode 100644
index 000000000..c35f9bffa
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QTimer, Property, Signal, Slot
+from PySide6.QtQml import QmlElement, QPyQmlPropertyValueSource
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class HappyBirthdaySong(QPyQmlPropertyValueSource):
+ name_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.m_target = None
+ self.m_name = ""
+ self.m_line = -1
+ self.m_lyrics = []
+
+ self.m_timer = QTimer(self)
+ self.m_timer.timeout.connect(self.advance)
+ self.m_timer.start(1000)
+
+ def setTarget(self, property):
+ self.m_target = property
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self.m_name
+
+ @name.setter
+ def name(self, n):
+ if self.m_name != n:
+ self.m_name = n
+ self.m_lyrics = ["Happy birthday to you,",
+ "Happy birthday to you,",
+ f"Happy birthday dear {self.m_name},",
+ "Happy birthday to you!",
+ ""]
+
+ @Slot()
+ def advance(self):
+ self.m_line = (self.m_line + 1) % len(self.m_lyrics)
+ self.m_target.write(self.m_lyrics[self.m_line])
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py
new file mode 100644
index 000000000..ea412a547
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the
+ qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example
+ from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
+
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty
+from happybirthdaysong import HappyBirthdaySong # noqa: F401
+
+
+app = QCoreApplication(sys.argv)
+engine = QQmlEngine()
+engine.addImportPath(Path(__file__).parent)
+component = QQmlComponent(engine)
+component.loadFromModule("People", "Main")
+party = component.create()
+if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+host = party.host
+print(f"{host.name} is having a birthday!")
+if isinstance(host, Boy):
+ print("He is inviting:")
+else:
+ print("She is inviting:")
+for g in range(party.guestCount()):
+ guest = party.guest(g)
+ name = guest.name
+
+ rsvp_date = None
+ attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False)
+ if attached:
+ rsvp_date = attached.rsvp.toString()
+ if rsvp_date:
+ print(f" {name} RSVP date: {rsvp_date}")
+ else:
+ print(f" {name} RSVP date: Hasn't RSVP'd")
+
+party.startParty()
+
+r = app.exec()
+
+del engine
+sys.exit(r)
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.py b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.py
new file mode 100644
index 000000000..503aaf65e
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlAnonymous, QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class Person(QObject):
+ name_changed = Signal()
+ shoe_size_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str, notify=name_changed, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(int, notify=shoe_size_changed, final=True)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
+
+
+@QmlElement
+class Boy(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+@QmlElement
+class Girl(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/binding/People/Main.qml
new file mode 100644
index 000000000..75add22af
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/People/Main.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ id: theParty
+
+ HappyBirthdaySong on announcement { name: theParty.host.name }
+
+ onPartyStarted: (time) => { console.log("This party started rockin' at " + time); }
+
+ host: Boy {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+
+ Boy {
+ name: "Leo Hodges"
+ BirthdayParty.rsvp: "2009-07-06"
+ }
+ Boy {
+ name: "Jack Smith"
+ }
+ Girl {
+ name: "Anne Brown"
+ BirthdayParty.rsvp: "2009-07-01"
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/binding/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/binding.pyproject b/examples/qml/tutorials/extending-qml-advanced/binding/binding.pyproject
new file mode 100644
index 000000000..fe2980fa9
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/binding.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "happybirthdaysong.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/binding/birthdayparty.py
new file mode 100644
index 000000000..a337d4a16
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/birthdayparty.py
@@ -0,0 +1,83 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QDate, QObject, ClassInfo, Property, QTime, Signal
+from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class BirthdayPartyAttached(QObject):
+
+ rsvp_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._rsvp = QDate()
+
+ @Property(QDate, notify=rsvp_changed)
+ def rsvp(self):
+ return self._rsvp
+
+ @rsvp.setter
+ def rsvp(self, d):
+ if self._rsvp != d:
+ self._rsvp = d
+ self.rsvp_changed.emit()
+
+
+@QmlElement
+@ClassInfo(DefaultProperty="guests")
+@QmlAttached(BirthdayPartyAttached)
+class BirthdayParty(QObject):
+
+ partyStarted = Signal(QTime)
+ host_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ def startParty(self):
+ self.partyStarted.emit(QTime.currentTime())
+
+ @Property(Person, notify=host_changed)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ if self._host != h:
+ self._host = h
+ self.host_changed.emit()
+
+ @Property(str)
+ def announcement(self):
+ return ""
+
+ @announcement.setter
+ def announcement(self, a):
+ print(a)
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+
+ @staticmethod
+ def qmlAttachedProperties(self, o):
+ return BirthdayPartyAttached(o)
+
+ guests = ListProperty(Person, appendGuest)
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/doc/binding.rst b/examples/qml/tutorials/extending-qml-advanced/binding/doc/binding.rst
new file mode 100644
index 000000000..5c0ed21be
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/doc/binding.rst
@@ -0,0 +1,17 @@
+.. _qml-binding-example:
+
+Extending QML - Binding Example
+===============================
+
+This example builds on the :ref:`qml-adding-types-example`,
+the :ref:`qml-attached-properties-example`,
+the :ref:`qml-default-property-example`,
+the :ref:`qml-inheritance-and-coercion-example`
+the :ref:`qml-object-and-list-property-types-example`
+and the :ref:`qml-valuesource-example`.
+
+Running the Example
+-------------------
+
+The ``main.py`` file in the example includes a simple shell application that
+loads and runs the QML snippet shown below.
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/happybirthdaysong.py b/examples/qml/tutorials/extending-qml-advanced/binding/happybirthdaysong.py
new file mode 100644
index 000000000..59ebfe4c6
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/happybirthdaysong.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QTimer, Property, Slot
+from PySide6.QtQml import QmlElement, QPyQmlPropertyValueSource
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class HappyBirthdaySong(QPyQmlPropertyValueSource):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ self.m_target = None
+ self.m_name = ""
+ self.m_line = -1
+ self.m_lyrics = []
+
+ self.m_timer = QTimer(self)
+ self.m_timer.timeout.connect(self.advance)
+ self.m_timer.start(1000)
+
+ def setTarget(self, property):
+ self.m_target = property
+
+ @Property(str)
+ def name(self):
+ return self.m_name
+
+ @name.setter
+ def name(self, n):
+ self.m_name = n
+ self.m_lyrics = ["Happy birthday to you,",
+ "Happy birthday to you,",
+ f"Happy birthday dear {self.m_name},",
+ "Happy birthday to you!",
+ ""]
+
+ @Slot()
+ def advance(self):
+ self.m_line = (self.m_line + 1) % len(self.m_lyrics)
+ self.m_target.write(self.m_lyrics[self.m_line])
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/main.py b/examples/qml/tutorials/extending-qml-advanced/binding/main.py
new file mode 100644
index 000000000..64929a807
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/main.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the qml/examples/qml/referenceexamples/binding example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
+
+from person import Boy, Girl # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+from happybirthdaysong import HappyBirthdaySong # noqa: F401
+
+
+if __name__ == "__main__":
+ app = QCoreApplication(sys.argv)
+ engine = QQmlEngine()
+ engine.addImportPath(Path(__file__).parent)
+ component = QQmlComponent(engine)
+ component.loadFromModule("People", "Main")
+ party = component.create()
+ if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+ host = party.host
+ print(f"{host.name} is having a birthday!")
+ if isinstance(host, Boy):
+ print("He is inviting:")
+ else:
+ print("She is inviting:")
+ for g in range(party.guestCount()):
+ guest = party.guest(g)
+ name = guest.name
+
+ rsvp_date = None
+ attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False)
+ if attached:
+ rsvp_date = attached.rsvp.toString()
+ if rsvp_date:
+ print(f" {name} RSVP date: {rsvp_date}")
+ else:
+ print(f" {name} RSVP date: Hasn't RSVP'd")
+
+ party.startParty()
+
+ r = app.exec()
+
+ del engine
+ sys.exit(r)
diff --git a/examples/qml/tutorials/extending-qml-advanced/binding/person.py b/examples/qml/tutorials/extending-qml-advanced/binding/person.py
new file mode 100644
index 000000000..a6942763a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/binding/person.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Signal
+from PySide6.QtQml import QmlAnonymous, QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class Person(QObject):
+ name_changed = Signal()
+ shoe_size_changed = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str, notify=name_changed)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ if self._name != n:
+ self._name = n
+ self.name_changed.emit()
+
+ @Property(int, notify=shoe_size_changed)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ if self._shoe_size != s:
+ self._shoe_size = s
+ self.shoe_size_changed.emit()
+
+
+@QmlElement
+class Boy(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+@QmlElement
+class Girl(Person):
+ def __init__(self, parent=None):
+ super().__init__(parent)
diff --git a/examples/qml/tutorials/extending-qml-advanced/extended/doc/extended.rst b/examples/qml/tutorials/extending-qml-advanced/extended/doc/extended.rst
new file mode 100644
index 000000000..745960535
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/extended/doc/extended.rst
@@ -0,0 +1,41 @@
+.. _qml-extension-objects-example:
+
+Extending QML - Extension Objects Example
+=========================================
+
+This example builds on the the :ref:`qml-adding-types-example`.
+
+Shows how to use QmlExtended decorator to provide an extension object to a
+QLineEdit without modifying or subclassing it.
+
+Firstly, the LineEditExtension class is registered with the QML system as an
+extension of QLineEdit. We declare a foreign type to do this as we cannot
+modify Qt's internal QLineEdit class.
+
+.. code-block:: python
+
+ @QmlNamedElement("QLineEdit")
+ @QmlExtended(LineEditExtension)
+ @QmlForeign(QLineEdit)
+ class LineEditForeign(QObject):
+
+
+Note the usage of ``QmlNamedElement()`` instead of ``QmlElement()``.
+``QmlElement()`` uses the name of the containing type by default,
+``LineEditExtension`` in this case. As the class being an extension class is
+an implementation detail, we choose the more natural name ``QLineEdit``
+instead.
+
+The QML engine then instantiates a QLineEdit.
+
+In QML, a property is set on the line edit that only exists in the
+``LineEditExtension`` class:
+
+.. code-block:: javascript
+
+ QLineEdit {
+ left_margin: 20
+ }
+
+The extension type performs calls on the ``QLineEdit`` that otherwise will not
+be accessible to the QML engine.
diff --git a/examples/qml/tutorials/extending-qml-advanced/extended/example.qml b/examples/qml/tutorials/extending-qml-advanced/extended/example.qml
new file mode 100644
index 000000000..e4af3bec5
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/extended/example.qml
@@ -0,0 +1,8 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import examples.extend 1.0
+
+QLineEdit {
+ left_margin: 20
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/extended/extended.pyproject b/examples/qml/tutorials/extending-qml-advanced/extended/extended.pyproject
new file mode 100644
index 000000000..127a3a76a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/extended/extended.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", "example.qml"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/extended/main.py b/examples/qml/tutorials/extending-qml-advanced/extended/main.py
new file mode 100644
index 000000000..6ee386401
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/extended/main.py
@@ -0,0 +1,95 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the qml/examples/qml/referenceexamples/extended example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QObject, QUrl, Property
+from PySide6.QtWidgets import QApplication, QLineEdit
+from PySide6.QtQml import (QQmlComponent, QQmlEngine, QmlForeign, QmlExtended,
+ QmlNamedElement)
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "examples.extend"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+class LineEditExtension(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._line_edit = parent
+
+ @Property(int)
+ def left_margin(self):
+ return self._line_edit.textMargins().left()
+
+ @left_margin.setter
+ def left_margin(self, m):
+ margins = self._line_edit.textMargins()
+ margins.setLeft(m)
+ self._line_edit.setTextMargins(margins)
+
+ @Property(int)
+ def right_margin(self):
+ return self._line_edit.textMargins().right()
+
+ @right_margin.setter
+ def right_margin(self, m):
+ margins = self._line_edit.textMargins()
+ margins.setRight(m)
+ self._line_edit.setTextMargins(margins)
+
+ @Property(int)
+ def top_margin(self):
+ return self._line_edit.textMargins().top()
+
+ @top_margin.setter
+ def top_margin(self, m):
+ margins = self._line_edit.textMargins()
+ margins.setTop(m)
+ self._line_edit.setTextMargins(margins)
+
+ @Property(int)
+ def bottom_margin(self):
+ return self._line_edit.textMargins().bottom()
+
+ @bottom_margin.setter
+ def bottom_margin(self, m):
+ margins = self._line_edit.textMargins()
+ margins.setBottom(m)
+ self._line_edit.setTextMargins(margins)
+
+
+@QmlNamedElement("QLineEdit")
+@QmlExtended(LineEditExtension)
+@QmlForeign(QLineEdit)
+class LineEditForeign(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+
+ qml_file = Path(__file__).parent / "example.qml"
+ url = QUrl.fromLocalFile(qml_file)
+ engine = QQmlEngine()
+ component = QQmlComponent(engine, url)
+ widget = component.create()
+ if not widget:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+
+ widget.show()
+ r = app.exec()
+ # Deleting the engine before it goes out of scope is required to make sure
+ # all child QML instances are destroyed in the correct order.
+ del engine
+ sys.exit(r)
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/methods/People/Main.qml
new file mode 100644
index 000000000..69b2119ab
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/People/Main.qml
@@ -0,0 +1,19 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import People
+
+BirthdayParty {
+ host: Person {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+ guests: [
+ Person { name: "Leo Hodges" },
+ Person { name: "Jack Smith" },
+ Person { name: "Anne Brown" }
+ ]
+
+ Component.onCompleted: invite("William Green")
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/methods/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/methods/birthdayparty.py
new file mode 100644
index 000000000..a3942b671
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/birthdayparty.py
@@ -0,0 +1,47 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property, Slot
+from PySide6.QtQml import QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class BirthdayParty(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ self._host = h
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+
+ @Slot(str)
+ def invite(self, name):
+ guest = Person(self)
+ guest.name = name
+ self.appendGuest(guest)
+
+ guests = ListProperty(Person, appendGuest)
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/doc/methods.rst b/examples/qml/tutorials/extending-qml-advanced/methods/doc/methods.rst
new file mode 100644
index 000000000..bda2ede5a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/doc/methods.rst
@@ -0,0 +1,15 @@
+.. _qml-methods-example:
+
+Extending QML - Methods Example
+===============================
+
+This example builds on the :ref:`qml-adding-types-example`,
+the :ref:`qml-object-and-list-property-types-example` and
+the :ref:`qml-inheritance-and-coercion-example`.
+
+The Methods Example has an additional method in the ``BirthdayParty`` class:
+``invite()``. ``invite()`` is decorated with ``@Slot`` so that it can be
+called from QML.
+
+In ``example.qml``, the ``invite()`` method is called
+in the ``QtQml.Component.completed()`` signal handler.
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/main.py b/examples/qml/tutorials/extending-qml-advanced/methods/main.py
new file mode 100644
index 000000000..fb656f266
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/main.py
@@ -0,0 +1,32 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the qml/examples/qml/referenceexamples/methods example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Person # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+app = QCoreApplication(sys.argv)
+engine = QQmlEngine()
+engine.addImportPath(Path(__file__).parent)
+component = QQmlComponent(engine)
+component.loadFromModule("People", "Main")
+party = component.create()
+if not party:
+ print(component.errors())
+ del engine
+ sys.exit(-1)
+host = party.host
+print(f"{host.name} is having a birthday!\nThey are inviting:")
+for g in range(party.guestCount()):
+ name = party.guest(g).name
+ print(f" {name}")
+del engine
+sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/methods.pyproject b/examples/qml/tutorials/extending-qml-advanced/methods/methods.pyproject
new file mode 100644
index 000000000..09942ebcc
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/methods.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "birthdayparty.py", "person.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/methods/person.py b/examples/qml/tutorials/extending-qml-advanced/methods/person.py
new file mode 100644
index 000000000..526eae714
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/methods/person.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property
+from PySide6.QtQml import QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class Person(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ self._name = n
+
+ @Property(int)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/People/Main.qml b/examples/qml/tutorials/extending-qml-advanced/properties/People/Main.qml
new file mode 100644
index 000000000..0600b3557
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/People/Main.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import People
+
+BirthdayParty {
+ host: Person {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+ guests: [
+ Person { name: "Leo Hodges" },
+ Person { name: "Jack Smith" },
+ Person { name: "Anne Brown" }
+ ]
+}
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/People/qmldir b/examples/qml/tutorials/extending-qml-advanced/properties/People/qmldir
new file mode 100644
index 000000000..a2bd9515a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/People/qmldir
@@ -0,0 +1,3 @@
+module People
+typeinfo coercion.qmltypes
+Main 1.0 Main.qml
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/birthdayparty.py b/examples/qml/tutorials/extending-qml-advanced/properties/birthdayparty.py
new file mode 100644
index 000000000..47dddc85d
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/birthdayparty.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property
+from PySide6.QtQml import QmlElement, ListProperty
+
+from person import Person
+
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class BirthdayParty(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ self._host = h
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+
+ guests = ListProperty(Person, appendGuest)
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/doc/properties.rst b/examples/qml/tutorials/extending-qml-advanced/properties/doc/properties.rst
new file mode 100644
index 000000000..16924cdcd
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/doc/properties.rst
@@ -0,0 +1,89 @@
+.. _qml-object-and-list-property-types-example:
+
+Extending QML - Object and List Property Types Example
+======================================================
+
+Exporting C++ Properties.
+
+This example builds on :ref:`qml-adding-types-example`.
+
+The Object and List Property Types example shows how to add object and list
+properties in QML. This example adds a BirthdayParty type that specifies a
+birthday party, consisting of a celebrant and a list of guests. People are
+specified using the People QML type built in the previous example.
+
+import examples.properties.people
+
+.. code-block:: javascript
+
+ BirthdayParty {
+ host: Person {
+ name: "Bob Jones"
+ shoe_size: 12
+ }
+ guests: [
+ Person { name: "Leo Hodges" },
+ Person { name: "Jack Smith" },
+ Person { name: "Anne Brown" }
+ ]
+ }
+
+Declare the BirthdayParty
+-------------------------
+
+The BirthdayParty class is declared like this:
+
+.. code-block:: python
+
+ from person import Person
+
+
+ # To be used on the @QmlElement decorator
+ # (QML_IMPORT_MINOR_VERSION is optional)
+ QML_IMPORT_NAME = "People"
+ QML_IMPORT_MAJOR_VERSION = 1
+
+
+ @QmlElement
+ class BirthdayParty(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._host = None
+ self._guests = []
+
+ @Property(Person)
+ def host(self):
+ return self._host
+
+ @host.setter
+ def host(self, h):
+ self._host = h
+
+ def guest(self, n):
+ return self._guests[n]
+
+ def guestCount(self):
+ return len(self._guests)
+
+ def appendGuest(self, guest):
+ self._guests.append(guest)
+
+ guests = ListProperty(Person, appendGuest)
+
+The class contains a member to store the celebrant object, and also a
+list member storing the Person instances.
+
+In QML, the type of a list properties - and the guests property is a list of
+people - are all of type :class:`~PySide6.QtQml.ListProperty`.
+``ListProperty`` is a simple value type that contains a set of functions.
+QML calls these functions whenever it needs to read from, write to or otherwise
+interact with the list. In addition to concrete lists like the people list used in this
+example, the use of ``ListProperty`` allows for "virtual lists" and other advanced
+scenarios.
+
+Running the Example
+-------------------
+
+The main.py file in the example includes a simple shell application that
+loads and runs the QML snippet shown at the beginning of this page.
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/main.py b/examples/qml/tutorials/extending-qml-advanced/properties/main.py
new file mode 100644
index 000000000..11757d5f3
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/main.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the qml/examples/qml/referenceexamples/properties example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtQml import QQmlComponent, QQmlEngine
+
+from person import Person # noqa: F401
+from birthdayparty import BirthdayParty # noqa: F401
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+
+ engine = QQmlEngine()
+ engine.addImportPath(Path(__file__).parent)
+ component = QQmlComponent(engine)
+ component.loadFromModule("People", "Main")
+
+ party = component.create()
+ if party:
+ print(f"{party.host} is having a birthday!\nThey are inviting:")
+ for g in range(party.guestCount()):
+ name = party.guest(g).name
+ print(f" {name}")
+ else:
+ print(component.errors())
+
+ del engine
+ sys.exit(0)
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/person.py b/examples/qml/tutorials/extending-qml-advanced/properties/person.py
new file mode 100644
index 000000000..526eae714
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/person.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Property
+from PySide6.QtQml import QmlElement
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "People"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class Person(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._name = ''
+ self._shoe_size = 0
+
+ @Property(str)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, n):
+ self._name = n
+
+ @Property(int)
+ def shoe_size(self):
+ return self._shoe_size
+
+ @shoe_size.setter
+ def shoe_size(self, s):
+ self._shoe_size = s
diff --git a/examples/qml/tutorials/extending-qml-advanced/properties/properties.pyproject b/examples/qml/tutorials/extending-qml-advanced/properties/properties.pyproject
new file mode 100644
index 000000000..adb34b2d0
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml-advanced/properties/properties.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "person.py", "birthdayparty.py",
+ "People/Main.qml", "People/qmldir"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml b/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml
new file mode 100644
index 000000000..6feef5633
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml
@@ -0,0 +1,26 @@
+// 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: aPieChart
+ anchors.centerIn: parent
+ width: 100; height: 100
+ name: "A simple pie chart"
+ color: "red"
+ }
+
+ Text {
+ anchors {
+ bottom: parent.bottom;
+ horizontalCenter: parent.horizontalCenter;
+ bottomMargin: 20
+ }
+ text: aPieChart.name
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/basics.py b/examples/qml/tutorials/extending-qml/chapter1-basics/basics.py
new file mode 100644
index 000000000..47d0a0e0c
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/basics.py
@@ -0,0 +1,68 @@
+# 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/chapter1-basics example from Qt v5.x"""
+
+import os
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import Property, Signal, QUrl
+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):
+
+ nameChanged = 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.Antialiasing, True)
+ painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
+
+ @Property(QColor, final=True)
+ def color(self):
+ return self._color
+
+ @color.setter
+ def color(self, value):
+ self._color = value
+
+ @Property(str, notify=nameChanged, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, value):
+ self._name = value
+
+
+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)
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.pyproject b/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.pyproject
new file mode 100644
index 000000000..869556bb8
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["basics.py", "app.qml"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/doc/chapter1-basics.rst b/examples/qml/tutorials/extending-qml/chapter1-basics/doc/chapter1-basics.rst
new file mode 100644
index 000000000..38233aae2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter1-basics/doc/chapter1-basics.rst
@@ -0,0 +1,107 @@
+.. _qml-chapter1-basics:
+
+Extending QML - Creating a New Type
+===================================
+
+This is the first of a series of 6 examples forming a tutorial
+about extending QML with Python.
+
+The Qt QML module provides a set of APIs for extending QML through Python
+extensions. You can write extensions to add your own QML types, extend existing
+Qt types, or call Python functions that are not accessible from ordinary QML
+code.
+
+This tutorial shows how to write a QML extension using Python that includes
+core QML features, including properties, signals and bindings. It also shows
+how extensions can be deployed through plugins.
+
+A common task when extending QML is to provide a new QML type that supports
+some custom functionality beyond what is provided by the built-in Qt Quick
+types. For example, this could be done to implement particular data models, or
+provide types with custom painting and drawing capabilities, or access system
+features like network programming that are not accessible through built-in QML
+features.
+
+In this tutorial, we will show how to use the C++ classes in the Qt Quick
+module to extend QML. The end result will be a simple Pie Chart display
+implemented by several custom QML types connected together through QML features
+like bindings and signals, and made available to the QML runtime through a
+plugin.
+
+To begin with, let's create a new QML type called ``PieChart`` that has two
+properties: a name and a color. We will make it available in an importable type
+namespace called ``Charts``, with a version of 1.0.
+
+We want this ``PieChart`` type to be usable from QML like this:
+
+.. code-block:: javascript
+
+ import Charts 1.0
+
+ PieChart {
+ width: 100; height: 100
+ name: "A simple pie chart"
+ color: "red"
+ }
+
+To do this, we need a C++ class that encapsulates this ``PieChart`` type and
+its two properties. Since QML makes extensive use of Qt's Meta-Object System
+this new class must:
+
+* Inherit from ``QObject``
+* Declare its properties using the ``Property`` decorator
+
+Class Implementation
+--------------------
+
+Here is our ``PieChart`` class, defined in ``basics.py``:
+
+.. literalinclude:: basics.py
+ :lineno-start: 21
+ :lines: 21-51
+
+The class inherits from ``QQuickPaintedItem`` because we want to override
+``QQuickPaintedItem.paint()`` to perform drawing operations with the
+``QPainter`` API. If the class just represented some data type and was not an
+item that actually needed to be displayed, it could simply inherit from
+``QObject``. Or, if we want to extend the functionality of an existing
+``QObject``-based class, it could inherit from that class instead.
+Alternatively, if we want to create a visual item that doesn't need to perform
+drawing operations with the ``QPainter`` API, we can just subclass
+``QQuickItem``.
+
+The ``PieChart`` class defines the two properties, ``name`` and ``color``, with
+the ``Property`` decorator, and overrides ``QQuickPaintedItem.paint()``. The
+``PieChart`` class is registered using the ``QmlElement`` decorator, to allow
+it to be used from QML. If you don't register the class, ``app.qml`` won't be
+able to create a ``PieChart``.
+
+QML Usage
+---------
+
+Now that we have defined the ``PieChart`` type, we will use it from QML. The
+``app.qml`` file creates a ``PieChart`` item and displays the pie chart's details
+using a standard QML ``Text`` item:
+
+.. literalinclude:: app.qml
+ :lineno-start: 7
+ :lines: 7-26
+
+Notice that although the color is specified as a string in QML, it is
+automatically converted to a ``QColor`` object for the PieChart ``color``
+property. Automatic conversions are provided for various other QML value types.
+For example, a string like "640x480" can be automatically converted to a
+``QSize`` value.
+
+We'll also create a main function that uses a ``QQuickView`` to run and display
+``app.qml``. Here is the application ``basics.py``:
+
+.. literalinclude:: basics.py
+ :lineno-start: 54
+ :lines: 54-68
+
+.. note:: You may see a warning `Expression ... depends on non-NOTIFYable properties:
+ PieChart.name`. This happens because we add a binding to the writable ``name``
+ property, but haven't yet defined a notify signal for it. The QML engine therefore
+ cannot update the binding if the ``name`` value changes. This is addressed in
+ the following chapters.
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml b/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml
new file mode 100644
index 000000000..d9477e253
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml
@@ -0,0 +1,32 @@
+// 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: aPieChart
+ anchors.centerIn: parent
+ width: 100; height: 100
+ color: "red"
+
+ onChartCleared: console.log("The chart has been cleared")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: aPieChart.clearChart()
+ }
+
+ Text {
+ anchors {
+ bottom: parent.bottom;
+ horizontalCenter: parent.horizontalCenter;
+ bottomMargin: 20
+ }
+ text: "Click anywhere to clear the chart"
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.pyproject b/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.pyproject
new file mode 100644
index 000000000..cdf33be7f
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["methods.py", "app.qml"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/doc/chapter2-methods.rst b/examples/qml/tutorials/extending-qml/chapter2-methods/doc/chapter2-methods.rst
new file mode 100644
index 000000000..245d0ddb2
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/doc/chapter2-methods.rst
@@ -0,0 +1,36 @@
+.. _qml-chapter2-methods:
+
+Extending QML - Connecting to C++ Methods and Signals
+=====================================================
+
+This is the second of a series of 6 examples forming a tutorial about extending
+QML with Python.
+
+Suppose we want ``PieChart`` to have a ``clearChart()`` method that erases the
+chart and then emits a ``chartCleared`` signal. Our ``app.qml`` would be able
+to call ``clearChart()`` and receive ``chartCleared()`` signals like this:
+
+.. literalinclude:: app.qml
+ :lineno-start: 4
+ :lines: 4-32
+
+To do this, we add a ``clearChart()`` method and a ``chartCleared()`` signal
+to our C++ class:
+
+.. literalinclude:: methods.py
+ :lineno-start: 54
+ :lines: 54-58
+
+The use of the ``Slot`` decorator makes the ``clearChart()`` method available
+to the Qt Meta-Object system, and in turn, to QML. The method simply changes
+the color to ``Qt::transparent``, repaints the chart, then emits the
+``chartCleared()`` signal:
+
+.. literalinclude:: methods.py
+ :lineno-start: 21
+ :lines: 21-24
+
+Now when we run the application and click the window, the pie chart disappears,
+and the application outputs::
+
+ qml: The chart has been cleared
diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/methods.py b/examples/qml/tutorials/extending-qml/chapter2-methods/methods.py
new file mode 100644
index 000000000..d455c317b
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter2-methods/methods.py
@@ -0,0 +1,75 @@
+# 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/chapter2-methods example from Qt v5.x"""
+
+import os
+from pathlib import Path
+import sys
+
+from PySide6.QtCore import Property, Signal, Slot, Qt, QUrl
+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()
+
+ 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.Antialiasing, True)
+ painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
+
+ @Property(QColor, final=True)
+ def color(self):
+ return self._color
+
+ @color.setter
+ def color(self, value):
+ self._color = value
+
+ @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.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)
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml b/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml
new file mode 100644
index 000000000..f1530516a
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml
@@ -0,0 +1,40 @@
+// 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"
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/bindings.py b/examples/qml/tutorials/extending-qml/chapter3-bindings/bindings.py
new file mode 100644
index 000000000..a9b61e7f1
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/bindings.py
@@ -0,0 +1,79 @@
+# 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/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.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.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)
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.pyproject b/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.pyproject
new file mode 100644
index 000000000..6e21f86f9
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["app.qml", "bindings.py"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/doc/chapter3-bindings.rst b/examples/qml/tutorials/extending-qml/chapter3-bindings/doc/chapter3-bindings.rst
new file mode 100644
index 000000000..3b7191191
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/doc/chapter3-bindings.rst
@@ -0,0 +1,54 @@
+.. _qml-chapter3-bindings:
+
+Extending QML - Adding Property Bindings
+========================================
+
+This is the third of a series of 6 examples forming a tutorial about extending
+QML with Python.
+
+Property binding is a powerful feature of QML that allows values of different
+types to be synchronized automatically. It uses signals to notify and update
+other types' values when property values are changed.
+
+Let's enable property bindings for the ``color`` property. That means if we
+have code like this:
+
+.. literalinclude:: app.qml
+ :lineno-start: 7
+ :lines: 7-40
+
+The ``color: chartA.color`` statement binds the ``color`` value of ``chartB``
+to the ``color`` of ``chartA.`` Whenever ``chartA`` 's ``color`` value changes,
+``chartB`` 's ``color`` value updates to the same value. When the window is
+clicked, the ``onClicked`` handler in the ``MouseArea`` changes the color of
+``chartA`` , thereby changing both charts to the color blue.
+
+It's easy to enable property binding for the ``color`` property. We add a
+``notify`` parameter to its ``Property`` decorator to indicate that a
+``colorChanged`` signal is emitted whenever the value changes.
+
+.. literalinclude:: bindings.py
+ :lineno-start: 39
+ :lines: 39-39
+
+.. literalinclude:: bindings.py
+ :lineno-start: 21
+ :lines: 21-26
+
+Then, we emit this signal in ``setColor()``:
+
+.. literalinclude:: bindings.py
+ :lineno-start: 43
+ :lines: 43-48
+
+It's important for ``setColor()`` to check that the color value has actually
+changed before emitting ``colorChanged().`` This ensures the signal is not
+emitted unnecessarily and also prevents loops when other types respond to the
+value change.
+
+The use of bindings is essential to QML. You should always add ``notify``
+signals for properties if they are able to be implemented, so that your
+properties can be used in bindings. Properties that cannot be bound cannot be
+automatically updated and cannot be used as flexibly in QML. Also, since
+bindings are invoked so often and relied upon in QML usage, users of your
+custom QML types may see unexpected behavior if bindings are not implemented.
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml
new file mode 100644
index 000000000..a5c5ff9fa
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml
@@ -0,0 +1,22 @@
+// 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)
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pyproject b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pyproject
new file mode 100644
index 000000000..af1cfefb7
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["app.qml", "customPropertyTypes.py"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py
new file mode 100644
index 000000000..659850f38
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py
@@ -0,0 +1,83 @@
+# 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)
diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/doc/chapter4-customPropertyTypes.rst b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/doc/chapter4-customPropertyTypes.rst
new file mode 100644
index 000000000..f7c3efb11
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/doc/chapter4-customPropertyTypes.rst
@@ -0,0 +1,73 @@
+.. _qml-chapter4-custompropertytypes:
+
+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:
+
+.. code-block:: python
+
+ class PieChart(QQuickPaintedItem):
+ chartIdChanged = Signal()
+
+ @Property(int, notify=chartIdChanged)
+ def chartId(self):
+ pass
+
+ @chartId.setter
+ def setChartId(self, chartId):
+ pass
+
+.. code-block:: javascript
+
+ // 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``:
+
+.. literalinclude:: app.qml
+ :lineno-start: 4
+ :lines: 4-22
+
+Like ``PieChart``, this new ``PieSlice`` type inherits from
+``QQuickPaintedItem``, is exposed via the ``QmlElement`` decorator and declares
+its properties with the ``Property`` decorator:
+
+.. literalinclude:: customPropertyTypes.py
+ :lineno-start: 21
+ :lines: 21-40
+
+To use it in ``PieChart``, we modify the ``color`` property declaration
+and associated method signatures:
+
+.. literalinclude:: customPropertyTypes.py
+ :lineno-start: 58
+ :lines: 58-65
+
+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:
+
+.. literalinclude:: customPropertyTypes.py
+ :lineno-start: 15
+ :lines: 15-18
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml b/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml
new file mode 100644
index 000000000..ac99d5a40
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml
@@ -0,0 +1,32 @@
+// 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
+ }
+ ]
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.pyproject b/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.pyproject
new file mode 100644
index 000000000..a3f89d575
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["app.qml", "listproperties.py"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst b/examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst
new file mode 100644
index 000000000..a98f18c81
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst
@@ -0,0 +1,47 @@
+.. _qml-chapter5-listproperties:
+
+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:
+
+.. literalinclude:: app.qml
+ :lineno-start: 4
+ :lines: 4-32
+
+To do this, we replace the ``pieSlice`` property in ``PieChart`` with a
+``slices`` property, declared as a class variable of the
+:class:`~PySide6.QtQml.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``:
+
+.. literalinclude:: listproperties.py
+ :lineno-start: 62
+ :lines: 62-65
+
+.. literalinclude:: listproperties.py
+ :lineno-start: 75
+ :lines: 75-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.
diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py b/examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py
new file mode 100644
index 000000000..98952cef1
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py
@@ -0,0 +1,97 @@
+# 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)
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py
new file mode 100644
index 000000000..3ab8bcc08
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Property
+from PySide6.QtQml import QmlElement, ListProperty
+from PySide6.QtQuick import QQuickItem
+
+from pieslice import PieSlice
+
+# 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(QQuickItem):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._slices = []
+ self._name = ''
+
+ @Property(str, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, name):
+ self._name = name
+
+ def slice(self, n):
+ return self._slices[n]
+
+ def sliceCount(self):
+ return len(self._slices)
+
+ def append_and_setparent(self, slice):
+ self._slices.append(slice)
+ slice.setParentItem(self)
+
+ slices = ListProperty(PieSlice, append_and_setparent)
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py
new file mode 100644
index 000000000..6f82f1f10
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Property, QRectF
+from PySide6.QtGui import QColor, QPainter, QPen
+from PySide6.QtQuick import QQuickPaintedItem
+from PySide6.QtQml import QmlElement
+
+# 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):
+ super().__init__(parent)
+
+ self._color = QColor()
+ self._from_angle = 0
+ self._angle_span = 0
+
+ @Property(QColor, final=True)
+ def color(self):
+ return self._color
+
+ @color.setter
+ def color(self, color):
+ self._color = QColor(color)
+
+ @Property(int, final=True)
+ def fromAngle(self):
+ return self._from_angle
+
+ @fromAngle.setter
+ def fromAngle(self, fromAngle):
+ self._from_angle = fromAngle
+
+ @Property(int, final=True)
+ def angleSpan(self):
+ return self._angle_span
+
+ @angleSpan.setter
+ def angleSpan(self, angleSpan):
+ self._angle_span = angleSpan
+
+ def paint(self, painter):
+ painter.setPen(QPen(self._color, 2))
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
+
+ rect = QRectF(0, 0, self.width(), self.height()).adjusted(1, 1, -1, -1)
+ painter.drawPie(rect, self._from_angle * 16, self._angle_span * 16)
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/plugins.png b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/plugins.png
new file mode 100644
index 000000000..8992e89c0
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/plugins.png
Binary files differ
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml b/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml
new file mode 100644
index 000000000..1a4772e15
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import Charts 1.0
+
+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
+ }
+ ]
+ }
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/chapter6-plugins.pyproject b/examples/qml/tutorials/extending-qml/chapter6-plugins/chapter6-plugins.pyproject
new file mode 100644
index 000000000..cc684401f
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/chapter6-plugins.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["app.qml", "Charts/piechart.py", "Charts/pieslice.py"]
+}
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst b/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst
new file mode 100644
index 000000000..a9d100812
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst
@@ -0,0 +1,26 @@
+.. _qml-chapter6-plugins-example:
+
+Extending QML - Plugins Example
+===============================
+
+This is the last of a series of 6 examples forming a tutorial
+about extending QML with Python.
+
+This example refers to the Python version of using a QML plugin in Python. The
+idea of plugins in Python is non-existent because Python modules are
+dynamically loaded anyway. We use this idea and our QML type registration
+decorators - ``QmlELement``/``QmlNamedElement`` - to register the QML modules as they
+are imported. The ``pyside6-qml`` tool does this for you by simply pointing to the
+``.qml`` file.
+
+.. image:: plugins.png
+ :width: 400
+ :alt: Plugins Example
+
+
+Running the Example
+-------------------
+
+.. code-block:: shell
+
+ pyside6-qml examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml -I examples/qml/tutorials/extending-qml/chapter6-plugins/Charts
diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/plugins.png b/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/plugins.png
new file mode 100644
index 000000000..8992e89c0
--- /dev/null
+++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/plugins.png
Binary files differ
diff --git a/examples/qml/usingmodel/doc/usingmodel.png b/examples/qml/usingmodel/doc/usingmodel.png
new file mode 100644
index 000000000..b4240951b
--- /dev/null
+++ b/examples/qml/usingmodel/doc/usingmodel.png
Binary files differ
diff --git a/examples/qml/usingmodel/doc/usingmodel.rst b/examples/qml/usingmodel/doc/usingmodel.rst
new file mode 100644
index 000000000..06a1b27b0
--- /dev/null
+++ b/examples/qml/usingmodel/doc/usingmodel.rst
@@ -0,0 +1,11 @@
+Using Model Example
+===================
+
+.. tags:: Android
+
+A Python application that demonstrates how to use a :ref:`QAbstractListModel`
+with QML.
+
+.. image:: usingmodel.png
+ :width: 400
+ :alt: Using Model Screenshot
diff --git a/examples/qml/usingmodel/usingmodel.py b/examples/qml/usingmodel/usingmodel.py
new file mode 100644
index 000000000..008a1b94b
--- /dev/null
+++ b/examples/qml/usingmodel/usingmodel.py
@@ -0,0 +1,74 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from dataclasses import dataclass
+from pathlib import Path
+import sys
+from PySide6.QtCore import QAbstractListModel, Qt, QUrl, QByteArray
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+from PySide6.QtQml import QmlElement, QmlSingleton
+
+
+QML_IMPORT_NAME = "PersonModel"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@dataclass
+class Person:
+ name: str
+ myrole: str
+
+
+@QmlElement
+@QmlSingleton
+class PersonModel (QAbstractListModel):
+ MyRole = Qt.UserRole + 1
+
+ def __init__(self, data, parent=None):
+ super().__init__(parent)
+ self._data = data
+
+ def roleNames(self):
+ roles = {
+ PersonModel.MyRole: QByteArray(b'myrole'),
+ Qt.DisplayRole: QByteArray(b'display')
+ }
+ return roles
+
+ def rowCount(self, index):
+ return len(self._data)
+
+ def data(self, index, role):
+ d = self._data[index.row()]
+ if role == Qt.DisplayRole:
+ return d.name
+ if role == Qt.DecorationRole:
+ return Qt.black
+ if role == PersonModel.MyRole:
+ return d.myrole
+ return None
+
+ @staticmethod
+ def create(engine):
+ data = [Person('Qt', 'myrole'), Person('PySide', 'role2')]
+ return PersonModel(data)
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+ view.setResizeMode(QQuickView.SizeRootObjectToView)
+
+ qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
+ view.setSource(QUrl.fromLocalFile(qml_file))
+ if view.status() == QQuickView.Error:
+ sys.exit(-1)
+ view.show()
+
+ r = 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(r)
diff --git a/examples/qml/usingmodel/usingmodel.pyproject b/examples/qml/usingmodel/usingmodel.pyproject
new file mode 100644
index 000000000..600cdb409
--- /dev/null
+++ b/examples/qml/usingmodel/usingmodel.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["view.qml", "usingmodel.py"]
+}
diff --git a/examples/qml/usingmodel/view.qml b/examples/qml/usingmodel/view.qml
new file mode 100644
index 000000000..e8b1fb2fb
--- /dev/null
+++ b/examples/qml/usingmodel/view.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import PersonModel
+
+ListView {
+ width: 100
+ height: 100
+ anchors.fill: parent
+ model: PersonModel
+ delegate: Component {
+ Rectangle {
+ height: 25
+ width: 100
+ Text {
+ text: display + ": " + myrole
+ }
+ }
+ }
+}