Spec-Zone.ru › Qt 5.15

Рекомендованные практики для QML и Qt Quick

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

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

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

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

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

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

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 можно использовать императивный 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 автоматически выбирает подходящее изображение для данного дисплея, при условии, что функция масштабирования для дисплеев с высоким разрешением (High DPI) явно включена.
  • Используйте изображения SVG для небольших значков. Хотя большие SVG могут быть медленными для отрисовки, маленькие работают хорошо. Векторные изображения исключают необходимость предоставления нескольких версий изображения, как это необходимо для растровых изображений.
  • Используйте значки на основе шрифтов, такие как Font Awesome. Они масштабируются для любого разрешения экрана и также позволяют изменение цвета. Пример Qt Quick Controls Text Editor хорошо демонстрирует это.

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

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

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

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

Spec-Zone.ru

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