Лучшие практики для QML и Qt Quick
Несмотря на все преимущества, которые предлагают QML и Qt Quick, они могут быть сложными в определенных ситуациях. В следующих разделах подробно описаны лучшие практики, которые помогут вам получить лучшие результаты при разработке приложений.
Пользовательские элементы управления UI
Гибкий и современный пользовательский интерфейс является ключевым для успеха любого приложения в современном мире, и именно здесь QML так подходит для дизайнера или разработчика. Qt предлагает самые базовые элементы управления UI, которые необходимы для создания гибкого и современного пользовательского интерфейса. Перед созданием собственного пользовательского элемента управления UI рекомендуется ознакомиться с этим списком элементов управления UI.
Помимо этих базовых элементов управления UI, предлагаемых самим Qt Quick, также доступен богатый набор элементов управления UI с Qt Quick Controls. Они отвечают наиболее распространённым случаям использования без каких-либо изменений и предлагают гораздо больше возможностей с помощью своих параметров настройки. В частности, Qt Quick Controls предоставляет параметры стилизации, которые соответствуют последним тенденциям в области дизайна пользовательского интерфейса. Если эти элементы управления UI не удовлетворяют потребностям вашего приложения, только тогда рекомендуется создавать пользовательский элемент управления.
Связанная информация
Рекомендации по написанию кода
См. Рекомендации по написанию кода 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. Создание файлов ресурсов подробно объясняет, как это сделать.
Связанная информация
Разделение 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.
Связанная информация
Использование макетов 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, ширина и высота достаточны.
Связанная информация
Безопасность типов
При объявлении свойств в 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 хорошо это демонстрирует.
При выполнении этих рекомендаций, пользовательский интерфейс вашего приложения должен масштабироваться в зависимости от разрешения дисплея.
Связанная информация
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.0/qtquick-bestpractices.html