aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/doc/src/cppintegration
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/doc/src/cppintegration')
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc78
-rw-r--r--src/qml/doc/src/cppintegration/extending-tutorial.qdoc20
-rw-r--r--src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc185
-rw-r--r--src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc88
4 files changed, 333 insertions, 38 deletions
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index 969bf4f7c0..d4fe08e37d 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -97,15 +97,18 @@ Types to QML} explains, the properties, methods and signals of any
QObject-derived class are accessible from QML code.
To register a QObject-derived class as an instantiable QML object type, add
-\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration and
+\c QML_ELEMENT or \c QML_NAMED_ELEMENT(<name>) to the class declaration. You
+also need to make adjustments in the build system. For qmake, add
\c {CONFIG += qmltypes}, a \c {QML_IMPORT_NAME}, and a
-\c QML_IMPORT_MAJOR_VERSION to your project file. This will register the class
-into the type namespace under the given major version, using either the class
-name or an explicitly given name as QML type name. The minor version(s) will
-be derived from any revisions attached to properties, methods, or signals. The
-default minor version is \c 0. You can explicitly restrict the type to be
-available only from specific minor versions by adding the
-\c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
+\c QML_IMPORT_MAJOR_VERSION to your project file. For CMake, the file containing
+the class should be part of a target set-up with
+\l{qt_add_qml_module}{qt_add_qml_module()}.
+This will register the class into the type namespace under the given major version,
+using either the class name or an explicitly given name as QML type name. The
+minor version(s) will be derived from any revisions attached to properties,
+methods, or signals. The default minor version is \c 0. You can explicitly
+restrict the type to be available only from specific minor versions by adding
+the \c QML_ADDED_IN_MINOR_VERSION() macro to the class declaration. Clients can
import suitable versions of the namespace in order to use the type.
For example, suppose there is a \c Message class with \c author and
@@ -127,26 +130,52 @@ This type can be registered by adding an appropriate type namespace and version
number to the project file. For example, to make the type available in the
\c com.mycompany.messaging namespace with version 1.0:
-\code
-CONFIG += qmltypes
-QML_IMPORT_NAME = com.mycompany.messaging
-QML_IMPORT_MAJOR_VERSION = 1
-\endcode
+\if defined(onlinedocs)
+ \tab {build-qt-app}{tab-cmake}{CMake}{selected}
+ \tab {build-qt-app}{tab-qmake}{qmake}{}
+ \tabcontent {tab-cmake}
+ \else
+ \section3 Using CMake
+\endif
+ \badcode
+ qt_add_qml_module(messaging
+ URI com.mycompany.messaging
+ VERSION 1.0
+ SOURCES
+ message.cpp message.h
+ )
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+ \tabcontent {tab-qmake}
+\else
+ \section3 Using QMake
+\endif
+ \code
+ CONFIG += qmltypes
+ QML_IMPORT_NAME = com.mycompany.messaging
+ QML_IMPORT_MAJOR_VERSION = 1
+ \endcode
+
+ If the header the class is declared in is not accessible from your project's
+ include path, you may have to amend the include path so that the generated
+ registration code can be compiled.
+
+ \code
+ INCLUDEPATH += com/mycompany/messaging
+ \endcode
+\if defined(onlinedocs)
+ \endtabcontent
+\endif
-If the header the class is declared in is not accessible from your project's
-include path, you may have to amend the include path so that the generated
-registration code can be compiled:
-\code
-INCLUDEPATH += com/mycompany/messaging
-\endcode
The type can be used in an \l{qtqml-syntax-basics.html#object-declarations}
{object declaration} from QML, and its properties can be read and written to,
as per the example below:
\qml
-import com.mycompany.messaging 1.0
+import com.mycompany.messaging
Message {
author: "Amelie"
@@ -334,10 +363,9 @@ classes directly, if this is either not possible or is complicated by some
other concerns, extension objects allow limited extension possibilities
without direct modifications.
-\e{Extension objects} add additional properties to an existing type. Extension
-objects can only add properties, not signals or methods. An extended type
-definition allows the programmer to supply an additional type, known as the
-\e{extension type}, when registering the class. The properties are transparently
+\e{Extension objects} add additional properties to an existing type. An extended
+type definition allows the programmer to supply an additional type, known as the
+\e{extension type}, when registering the class. Its members are transparently
merged with the original target class when used from within QML. For example:
\snippet referenceexamples/extended/example.qml 0
@@ -619,6 +647,8 @@ Item {
}
\endqml
+This is commonly referred to as "on" syntax.
+
Clients can register their own property value source types, but currently not
property value write interceptors.
diff --git a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
index 6e6d067ba6..b5f0676920 100644
--- a/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
+++ b/src/qml/doc/src/cppintegration/extending-tutorial.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -397,13 +397,11 @@ plugin with the Qt meta object system.
Here is the \c ChartsPlugin definition in \c chartsplugin.h:
-\snippet tutorials/extending-qml/chapter6-plugins/import/chartsplugin.h 0
+\snippet tutorials/extending-qml/chapter6-plugins/Charts/chartsplugin.h 0
-Then, we write a \c .pro project file that defines the project as a plugin library
-and specifies with DESTDIR that library files should be built into a \c {../Charts}
-directory.
+Then, we write a \c .pro project file that defines the project as a plugin library.
-\quotefile tutorials/extending-qml/chapter6-plugins/import/import.pro
+\quotefile tutorials/extending-qml/chapter6-plugins/Charts/Charts.pro
When building this example on Windows or Linux, the \c Charts directory will be
located at the same level as the application that uses our new import module.
@@ -413,7 +411,7 @@ plugin binary is copied to \c Contents/PlugIns in the the application bundle;
this path is set in \c {chapter6-plugins/app.pro}:
\quotefromfile tutorials/extending-qml/chapter6-plugins/app.pro
-\skipto osx
+\skipto macos
\printuntil }
To account for this, we also need to add this location as a
@@ -432,16 +430,16 @@ to the same location as the plugin binary.
The \c qmldir file declares the module name and the plugin that is made available
by the module:
-\quotefile tutorials/extending-qml/chapter6-plugins/import/qmldir
+\quotefile tutorials/extending-qml/chapter6-plugins/Charts/qmldir
Now we have a QML module that can be imported to any application, provided that the
QML engine knows where to find it. The example contains an executable that loads
\c app.qml, which uses the \c {import Charts 1.0} statement. Alternatively, you can
-load the QML file using the \l{Prototyping with qmlscene}{qmlscene tool}, setting the
-import path to the current directory so that it finds the \c qmldir file:
+load the QML file using the \l {Prototyping with the QML Runtime Tool}{qml tool},
+setting the import path to the current directory so that it finds the \c qmldir file:
\code
- qmlscene -I . app.qml
+ qml -I . app.qml
\endcode
The module "Charts" will be loaded by the QML engine, and the types provided by that
diff --git a/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc b/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
new file mode 100644
index 0000000000..64342fe1f8
--- /dev/null
+++ b/src/qml/doc/src/cppintegration/integrating-with-js-values-from-cpp.qdoc
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+\page qtqml-integrating-with-js-values-from-cpp.html
+\title Integrating with JavaScript values from C++
+\brief Description of how to load and access JavaScript from C++ code.
+
+The following classes can be used to load and access JavaSript from C++ code:
+
+\list
+
+ \li \l QJSValue, which acts as a container for Qt/JavaScript data types.
+ \li \l QJSManagedValue, which represents a value on the JavaScript heap
+ belonging to a \l QJSEngine.
+ \li \l QJSPrimitiveValue, which operates on primitive types in JavaScript semantics.
+\endlist
+
+Use QJSValue to transfer values to and from the engine, and use QJSManagedValue
+to interact with JavaScript values. Only use QJSPrimitiveValues if you have to
+emulate the semantics of JS primitive values in C++.
+
+\table
+ \header
+ \li QJSValue
+ \li QJSManagedValue
+ \li QJSPrimitiveValue
+ \row
+ \li Persistently store values
+ \li Short lived
+ \li Short lived
+ \row
+ \li Transport values to/from engine
+ \li Access properties
+ \li Only Primitives
+ \row
+ \li
+ \li Call methods
+ \li Basic arithmetic and comparison
+\endtable
+
+\section1 QJSValue as a Container Type
+
+\l QJSValue stores the Qt/JavaScript data types supported in ECMAScript including
+function, array and arbitrary object types as well as anything supported by
+QVariant. As a container, it can be used to pass values to and receive values
+from a QJSEngine.
+
+\snippet qtjavascript/integratingjswithcpp/exampleqjsascontainer.cpp qjs-as-container
+
+In case of a cache miss, \c undefined is returned. Otherwise, the cached value is
+returned. Note that implicit conversions (from QString and QJSValue::SpecialValue respectively)
+occur when the value is returned.
+
+QJSValue also has an API to interact with the contained value, but using
+QJSManagedValue is recommended.
+
+\section1 Primitive and Managed Values
+
+QJSValue and QJSManagedValue store values that can be either managed or primitive.
+In QML’s JS engine, a managed value can be thought of as a pointer to some data
+structure on the heap, whose memory is managed by the engine’s garbage collector.
+The actual content of primitive values is stored directly, using a technique
+called NaN-boxing that enables you to represent a NaN-value in multiple ways, even
+though only two are actually needed; one for signalling and one for quiet NaN-value.
+
+\table
+\header
+ \li Primitive Values
+ \li Managed Values
+\row
+ \li int
+ \li Function
+\row
+ \li double
+ \li Array
+\row
+ \li undefined
+ \li QVariant
+\row
+ \li null
+ \li string object
+\row
+ \li QString
+ \li
+\endtable
+
+A pointer to the engine can be obtained from a managed value, but not from a
+primitive one. When using QJSValue for its JavaScript API, you need access
+to the engine to evaluate JavaScript. For example, to run the \c call(args) function,
+you have to interpret it in the engine. This works, as the function is a managed
+value, and you can obtain the engine from it.
+
+Similarly, where the engine is needed when you call a function or
+access a property on a primitive number or string. Whenever you call a method on
+a primitive, an instance of its corresponding non-primitive objects is created.
+This is referred as boxing. When you write \c (42).constructor, that is equivalent
+to \c (new Number(42)).constructor, and it returns the constructor method of the
+global number object. Accordingly, if you write \c QJSValue(42).property("constructor"),
+you would expect to obtain a QJSValue containing that function. However, what you
+get is instead a QJSValue containing \c undefined.
+
+The QJSValue that you constructed contains only a primitive value, and thus you have
+no way to access the engine. You also can’t simply hardcode the property lookup
+for primitive values in QJSEngine, as in one engine you might set
+\e {Number.prototype.constructor.additionalProperty = "the Spanish Inquisition"}
+whereas in another \e {Number.prototype.constructor.additionalProperty = 42}.
+The end result would then clearly be unexpected.
+
+To ensure that property accesses always work, you would need to always store boxed
+values in QJSValue or store an additional pointer to the engine.
+
+However, this would be incompatible with how QJSValue is currently used, lead to
+pointless JS heap allocations when passing around primitives, and increase the
+size needed to store a QJSValue. Therefore, you should use \l QJSValue only for
+storage and \l QJSManagedValue to obtain the engine.
+
+\section1 QJSManagedValue
+
+QJSManagedValue is similar to QJSValue, with a few differences:
+
+\list
+\li The constructors (except for the default and move constructor2) require
+ passing a QJSEngine pointer.
+\li Methods like \c deleteProperty and \l isSymbol are added.
+\li If QJSManagedValue methods encounter an exception, they leave it intact.
+\endlist
+
+To obtain the engine in code, either you are in a scripting context where you’ve
+already got access to an engine to create new objects with \c QJSEngine::newObject
+and to evaluate expressions with \c QJSEngine::evaluate, or you want to evaluate
+some JavaScript in a QObject that has been registered with the engine. In the
+latter case, you can use \c qjsEngine(this) to obtain the currently active
+QJSEngine.
+
+QJSManagedValue also provides a few methods that have no equivalent in QJSEngine.
+
+In the example below, QJSManagedValue methods encounter an exception, and
+QJSEngine::catchError is used to handle the exception.
+
+\snippet qtjavascript/integratingjswithcpp/exampleqjsengine.cpp qjs-engine-example
+
+However, inside a method of a registered object, you might want to instead let
+the exception bubble up the call stack.
+
+QJSManagedValue should be temporarily created on the stack,
+and discarded once you don’t need to work any longer on the contained value.
+Since QJSValue can store primitive values in a more efficient way, QJSManagedValue
+should also not be used as an interface type which is the return or parameter type of
+functions, and the type of properties, as the engine does not treat it in a
+special way, and will not convert values to it (in contrast to QJSValue).
+
+\section1 QJSPrimitiveValue
+
+\l QJSPrimitiveValue can store any of the primitive types, and supports arithmetic
+operations and comparisons according to the ECMA-262 standard. It allows for
+low-overhead operations on primitives in contrast to QJSManagedValue, which always goes
+through the engine, while still yielding results that are indistinguishable
+from what the engine would return. As QJSPrimitiveValue is comparatively large, it
+is not recommended to store values.
+
+*/
diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
index 0a824bb5b5..6277b01af3 100644
--- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
+++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc
@@ -105,6 +105,88 @@ You can also connect to any signals or call methods defined in the component
using QMetaObject::invokeMethod() and QObject::connect(). See \l {Invoking QML Methods}
and \l {Connecting to QML Signals} below for further details.
+\section1 Accessing QML Objects via well-defined C++ Interfaces
+
+The best way of interacting with QML from C++ is to define an interface for
+doing so in C++ and accessing it in QML itself. With other methods, refactoring
+your QML code can easily lead to your QML / C++ interaction breaking. It also
+helps to reason about the interaction of QML and C++ code, as having it driven
+via QML can be more easily reasoned about by both users and tooling such as
+qmllint. Accessing QML from C++ will lead to QML code that cannot be understood
+without manually verifying that no outside C++ code is modifying a given QML
+component, and even then the extent of the access might change over time, making
+continued use of this strategy a maintenance burden.
+
+To let QML drive the interaction, first you need to define a C++ interface:
+
+\code
+class CppInterface : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ // ...
+};
+\endcode
+
+Using a QML-driven approach, this interface can be interacted with in two ways:
+
+\section2 Singletons
+
+One option is to register the interface as a singleton by adding the \l
+QML_SINGLETON macro to the interface, exposing it to all components. Following
+that, the interface becomes available via a simple import statement:
+
+\code
+import my.company.module
+
+Item {
+ Component.onCompleted: {
+ CppInterface.foo();
+ }
+}
+\endcode
+
+Use this approach if you need your interface in more places than the root component, as
+simply passing down an object would require explicitly passing it on to other
+components via a property or utilizing the slow and not recommended method of
+using \l {Unqualified access}{unqualified access}.
+
+\section2 Initial properties
+
+Another option is to mark the interface as uncreatable via \l QML_UNCREATABLE
+and supplying it to the root QML Component by using \l
+QQmlComponent::createWithInitialProperties() and a \l {Required
+Properties}{required property} on the QML end.
+
+Your root component may look something like this:
+
+\code
+import QtQuick
+
+Item {
+ required property CppInterface interface
+ Component.onCompleted: {
+ interface.foo();
+ }
+}
+\endcode
+
+Marking the property as required here protects the component against being
+created without the interface property being set.
+
+You can then initialize your component in the same way as outlined in \l
+{Loading QML Objects from C++} except using \c {createWithInitialProperties()}:
+
+\code
+ component.createWithInitialProperties(QVariantMap{{u"interface"_qs, QVariant::fromValue<CppInterface *>(new CppInterface)}});
+\endcode
+
+This method is to be preferred if you know that your interface only needs to be
+available to the root component. It also allows for connecting to signals and
+slots of the interface more easily on the C++ side.
+
+If neither of these methods suit your needs you may want to investigate the usage of
+\l {Using C++ Models with Qt Quick Views}{C++ models} instead.
\section1 Accessing Loaded QML Objects by Object Name
@@ -189,9 +271,9 @@ Notice the parameter and return type specified after the colon. You can use \l
{QML Basic Types}{basic types} and \l {QML Object Types}{object types} as type
names.
-If the type is omitted in QML, then you must specify QVariant as type with
-Q_RETURN_ARG() and Q_ARG() when calling QMetaObject::invokeMethod.
-
+If the type is omitted or specified as \c var in QML, then you must pass
+QVariant as type with Q_RETURN_ARG() and Q_ARG() when calling
+QMetaObject::invokeMethod.
\section2 Connecting to QML Signals