diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2023-05-04 12:02:21 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-05-11 10:49:31 +0000 |
commit | 667fdda3147b1556c13d6e9eab7155facbf4c0f6 (patch) | |
tree | cab571be65b4df08d29eb28f0474df68f4e6d156 | |
parent | 583ece6d54c7bb9d110da372084f99cf1787b0a8 (diff) |
QML basic reference examples: Add the tutorial texts
Take over the texts from C++ with adaptions for Python.
Task-number: PYSIDE-2206
Task-number: QTBUG-111033
Change-Id: Iedfb9b6cd62bf467f965c94e5dbb707a88456278
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
(cherry picked from commit c8a2f15dc74c5b3a0cbf3f8c1891f6961578d8b2)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
11 files changed, 330 insertions, 14 deletions
diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml b/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml index 415183596..6feef5633 100644 --- a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -//![0] + import Charts import QtQuick @@ -24,4 +24,3 @@ Item { text: aPieChart.name } } -//![0] 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 index d330f3b64..d9477e253 100644 --- a/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -//![0] + import Charts import QtQuick @@ -30,4 +30,3 @@ Item { text: "Click anywhere to clear the chart" } } -//![0] 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/chapter3-bindings/app.qml b/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml index ee24a428a..f1530516a 100644 --- a/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -//![0] + import Charts import QtQuick @@ -38,4 +38,3 @@ Item { text: "Click anywhere to change the chart color" } } -//![0] 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 index 954e6465c..a5c5ff9fa 100644 --- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -//![0] + import Charts import QtQuick @@ -20,4 +20,3 @@ Item { Component.onCompleted: console.log("The pie is colored " + chart.pieSlice.color) } -//![0] 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 index edbf3e770..ac99d5a40 100644 --- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -//![0] + import Charts import QtQuick @@ -30,4 +30,3 @@ Item { ] } } -//![0] 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..90cb41107 --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/doc/chapter5-listproperties.rst @@ -0,0 +1,46 @@ +.. _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 ``QQmlListProperty`` +type. The ``QQmlListProperty`` 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 ``QQmlListProperty`` 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 ``QQmlListProperty`` 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/chapter6-plugins/doc/chapter6-plugins.rst b/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst index aeffd7e9a..a9d100812 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/doc/chapter6-plugins.rst @@ -3,10 +3,15 @@ Extending QML - Plugins Example =============================== -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. +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 |