Spec-Zone.ru › Qt 6.0

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

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

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

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

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

Связанная информация

  • Qt Quick Controls
  • 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

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

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

Разделение UI и логики

Одна из ключевых целей большинства разработчиков приложений — создание поддерживаемого приложения. Один из способов достижения этой цели — разделение пользовательского интерфейса и бизнес-логики. Вот несколько причин, почему пользовательский интерфейс приложения должен быть написан на 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
import QtQuick.Controls

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
import QtQuick.Controls

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

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

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

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

Связанная информация

  • Интеграция QML и C++
  • Учебник по чат-приложению

Использование макетов Qt Quick

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

Должны

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

Не следует

  • Не задавайте предпочтительные размеры для элементов, которые предоставляют implicitWidth и implicitHeight, если их неявные размеры не удовлетворяют.
  • Не используйте якоря для элемента, являющегося непосредственным дочерним элементом макета. Вместо этого используйте 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
            }
        }
    }

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

Связанная информация

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

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

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

property var name
property var size
property var optionsMenu

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

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

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

property string name
property int size
property MyMenu optionsMenu

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

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

Предпочитайте декларативные привязки императивным назначениям

В QML можно использовать императивный JavaScript-код для выполнения задач, таких как реагирование на события ввода, отправка данных по сети и т. д. Императивный код имеет важное место в QML, но также важно понимать, когда его не следует использовать.

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

Rectangle {
    Component.onCompleted: color = "red"
}

Это имеет следующие недостатки:

  • Это медленно. Свойство color сначала оценивается со значением по умолчанию, а затем ещё раз с «красным» позже.
  • Это откладывает обнаружение ошибок, которые можно было бы найти на этапе компиляции, на время выполнения, что замедляет процесс разработки.
  • Это перезаписывает любые декларативные привязки, которые были установлены. В большинстве случаев это предполагается, но иногда это может быть непреднамеренным. См. Отладка перезаписи привязок для получения дополнительной информации.
  • Это мешает инструментам; например, Qt Quick Designer не поддерживает JavaScript.

Код можно переписать в декларативную привязку:

Rectangle {
    color: "red"
}

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

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

Граф сцены

Для получения информации о графическом представлении сцен Qt Quick, см. Графическое представление сцен Qt Quick.

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

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

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

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

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

Связанная информация

  • Пример галереи
  • Пример текстового редактора
  • Font Awesome
  • Масштабируемость
  • Высокое DPI

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

Spec-Zone.ru

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