Spec-Zone.ru › Qt 5.13

Лучшие практики для QML и Qt Quick

Несмотря на все преимущества, которые предлагают QML и Qt Quick, они могут быть сложными в определенных ситуациях. В следующих разделах подробно описаны лучшие практики, которые помогут вам получить лучшие результаты при разработке приложений.

Пользовательские элементы управления UI

Гибкий и современный интерфейс пользователя является ключом к успеху любого приложения в современном мире, и именно здесь QML так подходит для дизайнеров и разработчиков. Qt предоставляет наиболее базовые элементы управления UI, необходимые для создания гибкого и современного интерфейса. Рекомендуется ознакомиться с этим списком элементов управления UI перед созданием собственного пользовательского элемента управления UI.

Помимо этих базовых элементов управления UI, предлагаемых самим Qt Quick, также доступен богатый набор элементов управления UI с Qt Quick Controls 2. Они удовлетворяют наиболее распространённым случаям использования без каких-либо изменений и предлагают гораздо больше возможностей с помощью опций настройки. В частности, Qt Quick Controls 2 предоставляет варианты стилей, которые соответствуют последним тенденциям в дизайне интерфейса пользователя. Если эти элементы управления UI не удовлетворяют потребности вашего приложения, только тогда рекомендуется создавать пользовательский элемент управления.

Дополнительная информация

  • Qt Quick Controls 2
  • Qt Quick

Рекомендации по кодированию

См. Рекомендации по кодированию QML.

Архивирование ресурсов приложения

Большинство приложений зависят от ресурсов, таких как изображения и значки, чтобы обеспечить богатый пользовательский опыт. Часто бывает сложно сделать эти ресурсы доступными для приложения независимо от целевой ОС. Большинство популярных ОС применяют более строгие политики безопасности, которые ограничивают доступ к файловой системе, затрудняя загрузку этих ресурсов. В качестве альтернативы Qt предлагает собственную систему ресурсов, встроенную в исполняемый файл приложения, обеспечивая доступ к ресурсам приложения независимо от целевой ОС.

Например, рассмотрите следующую структуру каталогов проекта:

project
├── images
│   ├── image1.png
│   └── image2.png
├── project.pro
└── qml
    └── main.qml

Следующая запись в project.pro гарантирует, что ресурсы включены в исполняемый файл приложения, делая их доступными при необходимости:

RESOURCES += \
    qml/main.qml \
    images/image1.png \
    images/image2.png

Более удобный подход заключается в использовании синтаксиса подстановок для выбора нескольких файлов сразу:

RESOURCES += \
    $$files(qml/*.qml) \
    $$files(images/*.png)

Этот подход удобен для приложений, зависящих от ограниченного числа ресурсов. Однако всякий раз, когда добавляется новый файл в RESOURCES с использованием этого подхода, все остальные файлы в RESOURCES также перекомпилируются. Это может быть неэффективно, особенно для больших наборов файлов. В этом случае лучшим подходом является разделение каждого типа ресурсов в отдельный файл .qrc. Например, фрагмент кода выше можно изменить следующим образом:

qml.files = $$files(*.qml)
qml.prefix = /qml
RESOURCES += qml

images.files = $$files(*.png)
images.prefix = /images
RESOURCES += images

Теперь, когда изменяется файл QML, необходимо перекомпилировать только файлы QML.

Иногда может потребоваться больше контроля над путем для конкретного файла, управляемого системой ресурсов. Например, если мы хотим дать image2.png псевдоним, нам нужно переключиться на явный файл .qrc. Создание файлов ресурсов подробно объясняет, как это сделать.

Дополнительная информация

  • Система ресурсов Qt

Разделение интерфейса пользователя от логики

Одна из ключевых целей большинства разработчиков приложений — создание поддерживаемого приложения. Один из способов достижения этой цели — разделение пользовательского интерфейса от бизнес-логики. Ниже приведены несколько причин, почему пользовательский интерфейс приложения должен быть написан на QML:

  • Декларативные языки хорошо подходят для определения пользовательских интерфейсов.
  • Код QML проще писать, так как он менее громоздкий, чем C++, и не является строго типизированным. Это также делает его отличным языком для прототипирования, качество, которое имеет решающее значение при сотрудничестве с дизайнерами, например.
  • JavaScript можно легко использовать в QML для реагирования на события.

Являясь строго типизированным языком, C++ лучше всего подходит для логики приложения. Как правило, такой код выполняет задачи, такие как сложные вычисления или обработка данных, которые выполняются быстрее на C++, чем на QML.

Qt предлагает различные подходы к интеграции кода QML и C++ в приложение. Типичный случай использования — отображение списка данных в пользовательском интерфейсе. Если набор данных статичен, прост и/или мал, модель, написанная на 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

Используйте C++ для динамических наборов данных, которые являются большими или часто изменяются.

Взаимодействие с QML из C++

Хотя Qt позволяет манипулировать QML из C++, это не рекомендуется. Чтобы объяснить почему, давайте рассмотрим упрощённый пример.

Получение ссылок из QML

Предположим, что мы пишем пользовательский интерфейс для страницы настроек:

import QtQuick 2.13
import QtQuick.Controls 2.13

Page {
    Button {
        text: qsTr("Restore default settings")
    }
}

Мы хотим, чтобы кнопка выполняла действия в C++ при нажатии. Мы знаем, что объекты в QML могут испускать сигналы изменения так же, как и в C++, поэтому мы даём кнопке имя объекта, чтобы мы могли найти её из C++:

Button {
    objectName: "restoreDefaultsButton"
    text: qsTr("Restore default settings")
}

Затем в C++ мы находим этот объект и подключаемся к его сигналу изменения:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QSettings>

class Backend : public QObject
{
    Q_OBJECT

public:
    Backend() {}

public slots:
    void restoreDefaults() {
        settings.setValue("loadLastProject", QVariant(false));
    }

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"

С этим подходом ссылки на объекты «получаются» из QML. Проблема с этим заключается в том, что уровень логики C++ зависит от уровня представления QML. Если мы переименуем QML таким образом, что objectName изменится, или какое-либо другое изменение нарушит возможность C++ найти объект QML, наш рабочий процесс становится намного более сложным и утомительным.

Передача ссылок в QML

Переименование QML намного проще, чем переименование C++, поэтому для того, чтобы минимизировать проблемы с обслуживанием, мы должны стремиться к тому, чтобы типы C++ были максимально независимы от QML. Этого можно достичь, «передавая» ссылки на типы C++ в QML:

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();
}

Затем QML напрямую вызывает слот C++:

import QtQuick 2.13
import QtQuick.Controls 2.13

Page {
    Button {
        text: qsTr("Restore default settings")
        onClicked: backend.restoreDefaults()
    }
}

С этим подходом C++ остаётся неизменным в случае необходимости переименования QML в будущем.

В приведённом выше примере мы задаём контекстную переменную в корневом контексте, чтобы экспонировать объект C++ в QML. Это означает, что свойство доступно для каждого компонента, загруженного движком. Контекстные свойства полезны для объектов, которые должны быть доступны сразу после загрузки QML и не могут быть созданы в QML.

Для краткого руководства по выбору правильного подхода к экспонированию типов C++ в QML см. Выбор правильного метода интеграции между C++ и QML.

Дополнительная информация

  • Интеграция QML и C++
  • Пример чат-приложения

Использование Qt Quick Layouts

Qt предлагает Qt Quick Layouts для визуального расположения элементов Qt Quick в макете. В отличие от альтернативных позиционеров элементов, Qt Quick Layouts также может изменять размер своих дочерних элементов при изменении размера окна. Хотя Qt Quick Layouts часто являются предпочтительным выбором для большинства случаев использования, при их использовании необходимо учитывать следующие «следует» и «не следует»:

Следует

  • Используйте anchors или свойства width и height, чтобы указать размер макета относительно родительского элемента, который не является макетом.
  • Используйте присоединенное свойство Layout для установки атрибутов размера и выравнивания непосредственных дочерних элементов макета.

Не следует

  • Не определяйте предпочтительные размеры для элементов, предоставляющих implicitWidth и implicitHeight, если их неявные размеры не удовлетворяют требованиям.
  • Не используйте anchors на элементе, который является непосредственным дочерним элементом макета. Вместо этого используйте Layout.preferredWidth и Layout.preferredHeight:
    RowLayout {
        id: layout
        anchors.fill: parent
        spacing: 6
        Rectangle {
            color: 'azure'
            Layout.fillWidth: true
            Layout.minimumWidth: 50
            Layout.preferredWidth: 100
            Layout.maximumWidth: 300
            Layout.minimumHeight: 150
            Text {
                anchors.centerIn: parent
                text: parent.width + 'x' + parent.height
            }
        }
        Rectangle {
            color: 'plum'
            Layout.fillWidth: true
            Layout.minimumWidth: 100
            Layout.preferredWidth: 200
            Layout.preferredHeight: 100
            Text {
                anchors.centerIn: parent
                text: parent.width + 'x' + parent.height
            }
        }
    }

Примечание: Макеты и anchors — это типы объектов, которые занимают больше памяти и времени инициализации. Избегайте их использования (особенно в делегатах списков и таблиц и стилях для элементов управления), если достаточно простых связей со свойствами x, y, width и height.

Дополнительная информация

  • Позиционеры элементов
  • Обзор Qt Quick Layouts

Безопасность типов

При объявлении свойств в QML легко и удобно использовать тип «var»:

property var name
property var size
property var optionsMenu

Однако этот подход имеет несколько недостатков:

  • Если присваивается значение неправильного типа, ошибка будет указывать на местоположение объявления свойства, а не на местоположение, где свойство было присвоено. Это замедляет процесс разработки, затрудняя отслеживание ошибок.
  • Статический анализ для обнаружения ошибок, упомянутых выше, невозможен.
  • Фактический базовый тип свойства не всегда сразу понятен читателю.

Вместо этого всегда используйте фактический тип, где это возможно:

property string name
property int size
property MyMenu optionsMenu

Производительность

Дополнительную информацию о производительности в QML и Qt Quick см. в Рекомендации и советы по производительности.

Инструменты и утилиты

Дополнительную информацию об полезных инструментах и утилитах, которые упрощают работу с QML и Qt Quick, см. в Инструменты и утилиты Qt Quick.

Граф сцены

Дополнительную информацию о графике сцены Qt Quick см. в Граф сцены Qt Quick.

Масштабируемые пользовательские интерфейсы

По мере улучшения разрешений дисплеев всё более важным становится создание масштабируемого пользовательского интерфейса приложения. Один из подходов к этому — сохранение нескольких копий пользовательского интерфейса для различных разрешений экрана и загрузка соответствующей копии в зависимости от доступного разрешения. Хотя это работает довольно хорошо, это увеличивает нагрузку на обслуживание.

Qt предлагает лучшее решение этой проблемы и рекомендует разработчикам приложений следовать этим советам:

  • Используйте якоря или модуль Qt Quick Layouts для размещения визуальных элементов.
  • Не указывайте явную ширину и высоту для визуального элемента.
  • Предоставьте ресурсы пользовательского интерфейса, такие как изображения и значки, для каждой поддерживаемой вашим приложением разрешающей способности экрана. Пример галереи Qt Quick Controls 2 хорошо демонстрирует это, предоставляя qt-logo.png для @2x, @3x, и @4x разрешений, что позволяет приложению адаптироваться к экранам высокой разрешающей способности. Qt автоматически выбирает подходящее изображение для данного дисплея, при условии, что функция масштабирования высокой плотности пикселей явно включена.
  • Используйте изображения SVG для небольших значков. Хотя большие SVG могут быть медленными для отрисовки, маленькие работают хорошо. Векторные изображения исключают необходимость предоставления нескольких версий изображения, как это необходимо с растровыми изображениями.
  • Используйте значки на основе шрифтов, такие как Font Awesome. Они масштабируются для любого разрешения экрана и также позволяют изменение цвета. Пример Qt Quick Controls 2 Text Editor хорошо демонстрирует это.

При выполнении этих условий пользовательский интерфейс вашего приложения должен масштабироваться в зависимости от разрешения дисплея.

Справочная информация

  • Пример галереи
  • Пример текстового редактора
  • Font Awesome
  • Масштабируемость
  • Дисплей высокой плотности пикселей

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.13/qtquick-bestpractices.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API