aboutsummaryrefslogtreecommitdiffstats
path: root/examples/qml/tutorials/extending-qml
diff options
context:
space:
mode:
Diffstat (limited to 'examples/qml/tutorials/extending-qml')
-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
27 files changed, 1041 insertions, 0 deletions
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