diff options
Diffstat (limited to 'src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc')
-rw-r--r-- | src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc | 269 |
1 files changed, 217 insertions, 52 deletions
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 6327ea67e6..7d4f564a6f 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -27,13 +27,13 @@ /*! \page qtquick-bestpractices.html -\title Qt Quick Best Practices -\brief Lists the practices that works best for Qt Quick +\title Best Practices for QML and Qt Quick +\brief Lists best practices for working with QML and Qt Quick -Besides all the benefits that Qt Quick offers, it can be challenging in certain -situations. For example, a Qt Quick application with a large codebase can be -painful to maintain if not organized well. The following sections elaborate -on some of the best practices that will help you get better results. +Despite all of the benefits that QML and Qt Quick offer, they can be +challenging in certain situations. The following sections elaborate on some of +the best practices that will help you get better results when developing +applications. \section1 Custom UI Controls @@ -48,7 +48,7 @@ controls are also available with Qt Quick Controls 2. They cater to the most common use cases without any change, and offer a lot more possibilities with their customization options. In particular, Qt Quick Controls 2 provides styling options that align with the latest UI design trends. If these UI controls do not -satisfy to your application's needs, only then it is recommended to create a +satisfy your application's needs, only then it is recommended to create a custom control. @@ -80,79 +80,229 @@ rich user experience. It can often be a challenge to make these resources available to the application regardless of the target OS. Most popular OS-es employ stricter security policies that restrict access to the file system, making it harder to load these resources. As an alternative, Qt offers its own -resource system that is built into the application binary, enabling access to -the application's resources regardless of the target OS. +\l {The Qt Resource System}{resource system} that is built into the +application binary, enabling access to the application's resources regardless +of the target OS. -It is recommended to bundle your application's resources (including the -\c .qml files) into a resource file (\c.qrc). For example, the following entry -in the qmake project file ensures that the resources are built into the -application binary, making them available when needed: +For example, consider the following project directory structure: \badcode - RESOURCES += resources.qrc +project +├── images +│ ├── image1.png +│ └── image2.png +├── project.pro +└── qml + └── main.qml \endcode -If your application depends on a limited number of resources, you could list -them directly in the project file. +The following entry in \c project.pro ensures that the resources are built into +the application binary, making them available when needed: \badcode - RESOURCES += a.qml b.png + RESOURCES += \ + qml/main.qml \ + images/image1.png \ + images/image2.png \endcode -In such a case, qmake creates the \c qmake_intermediate.qrc build artifact, -which you could rename and use the \c{RESOURCES += resource-set.qrc} entry -instead. +A more convenient approach is to use the +\l {files(pattern[, recursive=false])}{wildcard syntax} to select several files +at once: -You could go a step further by using one \c .qrc file for each resource type. -For example, list the \c .qml files in \c files.qrc, images in -\c images.qrc, fonts in \c fonts.qrc, and so on. That way, you need not -recompile the QML files when you, for example, add an image to the list in -\c images.qrc. +\badcode + RESOURCES += \ + $$files(qml/*.qml) \ + $$files(images/*.png) +\endcode + +This approach is convenient for applications that depend on a limited number +of resources. However, whenever a new file is added to \c RESOURCES using this +approach, it causes \e all of the other files in \c RESOURCES to be recompiled +as well. This can be inefficient, especially for large sets of files. +In this case, a better approach is to separate each type of resource into its +own \l {Resource Collection Files (.qrc)}{.qrc} file. For example, the snippet +above could be changed to the following: + +\badcode + qml.files = $$files(*.qml) + qml.prefix = /qml + RESOURCES += qml + + images.files = $$files(*.png) + images.prefix = /images + RESOURCES += images +\endcode + +Now, whenever a QML file is changed, only the QML files have to be recompiled. + +Sometimes it can be necessary to have more control over the path for a +specific file managed by the resource system. For example, if we wanted to give +\c image2.png an alias, we would need to switch to an explicit \c .qrc file. +\l {Creating Resource Files} explains how to do this in detail. \section2 Related Information \list \li \l{The Qt Resource System} \endlist -\section1 Application UI and Business Logic +\section1 Separate UI from Logic One of the key goals that most application developers want to achieve is to create a maintainable application. One of the ways to achieve this goal is -to separate the UI from the business logic. The following are a few reasons -why application's UI should be in QML: +to separate the user interface from the business logic. The following are a few +reasons why an application's UI should be written in QML: \list - \li QML is a declarative language, which suits best for defining UIs. - \li It's easier to embed JavaScript in QML to respond to events, for example. - \li QML is faster to code as it is not strongly typed. + \li Declarative languages are strongly suited for defining UIs. + \li QML code is simpler to write, as it is less verbose than C++, and is not + strongly typed. This also results in it being an excellent language to + prototype in, a quality that is vital when collaborating with designers, + for example. + \li JavaScript can easily be used in QML to respond to events. \endlist -On the other hand, C++ being a strongly typed language, suits best for defining -business logic. Typically, such code performs tasks such as complex calculations -or larger data processing, which can be faster with C++ than with QML. +Being a strongly typed language, C++ is best suited for an application's logic. +Typically, such code performs tasks such as complex calculations +or data processing, which are faster in C++ than QML. Qt offers various approaches to integrate QML and C++ code in an application. -In most cases, the C++ part (business logic) provides the data model to the QML -part (UI), which presents it in a readable form. It is often a challenge to -decide when to use this approach. It is recommended to use QML -if the data model is static, simple, and small, as C++ could be overkill. -Use C++ if the application depends on a dynamic, large, and complex data model. +A typical use case is displaying a list of data in a user interface. +If the data set is static, simple, and/or small, a model written in QML can be +sufficient. -\omit -examples snippets of simpler and complex data models. -\endomit +The following snippet demonstrates examples of models written in QML: + +\qml + model: ListModel { + ListElement { name: "Item 1" } + ListElement { name: "Item 2" } + ListElement { name: "Item 3" } + } + + model: [ "Item 1", "Item 2", "Item 3" ] + + model: 10 +\endqml + +Use \l {QAbstractItemModel Subclass}{C++} for dynamic data sets that are large +or frequently modified. + +\section2 Interacting with QML from C++ + +Although Qt enables you to manipulate QML from C++, it is not recommended to do +so. To explain why, let's take a look at a simplified example. Suppose we were +writing the UI for a settings page: + +\qml + import QtQuick 2.11 + import QtQuick.Controls 2.4 + + Page { + Button { + text: qsTr("Restore default settings") + } + } +\endqml + +We want the button to do something in C++ when it is clicked. We know objects +in QML can emit change signals just like they can in C++, so we give the button +an \l [QML]{QtObject::}{objectName} so that we can find it from C++: + +\qml + Button { + objectName: "restoreDefaultsButton" + text: qsTr("Restore default settings") + } +\endqml + +Then, in C++, we find that object and connect to its change signal: + +\code + #include <QGuiApplication> + #include <QQmlApplicationEngine> + #include <QSettings> + + class Backend : public QObject + { + Q_OBJECT + + public: + Backend() {} -\section2 Interaction Path + public slots: + void restoreDefaults() { + settings.setValue("loadLastProject", QVariant(false)); + } -Although Qt enables you to manipulate QML from C++, it is not a recommended -approach, as debugging such code can be painful. The QML engine works out -a lot of the details on the fly, so manipulating the QML items from C++ could -lead to unexpected results. This approach also makes the C++ code rely on the -QML code to provide certain properties or objects, making it difficult to -refactor the QML code without breaking the C++ code. Moreover, such C++ code -cannot reused with other QML code. To avoid all of these hassles and have a -maintainable application, it is recommended to always use the C++ to QML path -and not the other way around. + private: + QSettings settings; + }; + + int main(int argc, char *argv[]) + { + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + Backend backend; + + QObject *rootObject = engine.rootObjects().first(); + QObject *restoreDefaultsButton = rootObject->findChild<QObject*>("restoreDefaultsButton"); + QObject::connect(restoreDefaultsButton, SIGNAL(clicked()), + &backend, SLOT(restoreDefaults())); + + return app.exec(); + } + + #include "main.moc" +\endcode + +The problem with this approach is that the C++ logic layer depends on the QML +presentation layer. If we were to refactor the QML in such a way that the +\c objectName changes, or some other change breaks the ability for the C++ +to find the QML object, our workflow becomes much more complicated and tedious. + +Refactoring QML is a lot easier than refactoring C++, so in order to make +maintenance pain-free, we should strive to keep C++ types unaware of QML as +much as possible. This can be achieved by exposing the C++ types to QML: + +\code + int main(int argc, char *argv[]) + { + QGuiApplication app(argc, argv); + + Backend backend; + + QQmlApplicationEngine engine; + engine.rootContext()->setContextProperty("backend", &backend); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); + } +\endcode + +The QML then calls the C++ slot directly: + +\qml + import QtQuick 2.11 + import QtQuick.Controls 2.4 + + Page { + Button { + text: qsTr("Restore default settings") + onClicked: backend.restoreDefaults() + } + } +\endqml + +With this approach, the C++ remains unchanged in the event that the QML needs +to be refactored in the future. \section2 Related Information \list @@ -201,4 +351,19 @@ properties are enough. \li \l{Item Positioners} \li \l{Qt Quick Layouts Overview} \endlist + +\section1 Performance + +For information on performance in QML and Qt Quick, +see \l {Performance Considerations And Suggestions}. + +\section1 Tools and Utilities + +For information on useful tools and utilies that make working with QML and +Qt Quick easier, see \l {Qt Quick Tools and Utilities}. + +\section1 Scene Graph + +For information on Qt Quick's scene graph, see \l {Qt Quick Scene Graph}. + */ |