Рекомендации по разработке приложений на QML и Qt Quick
Несмотря на все преимущества, которые предоставляют QML и Qt Quick, их использование может быть сложным в определенных ситуациях. В следующих разделах подробно рассмотрены некоторые лучшие практики, которые помогут вам добиться лучших результатов при разработке приложений.
Настраиваемые элементы управления пользовательским интерфейсом
Интуитивно понятный и современный пользовательский интерфейс является ключевым для успеха любого приложения в современном мире, и именно здесь QML становится особенно полезным для дизайнеров и разработчиков. Qt предлагает базовые элементы управления пользовательским интерфейсом, необходимые для создания интуитивно понятного и современного интерфейса. Рекомендуется ознакомиться со списком этих элементов управления, прежде чем создавать собственные.
Помимо этих базовых элементов управления, предлагаемых самим Qt Quick, с Qt Quick Controls 2 доступен богатый набор элементов управления пользовательским интерфейсом. Они удовлетворяют наиболее распространённым случаям использования без изменений и предлагают больше возможностей для настройки. В частности, Qt Quick Controls 2 предоставляет варианты стилизации, соответствующие последним тенденциям дизайна пользовательского интерфейса. Если эти элементы управления не удовлетворяют требованиям вашего приложения, только тогда рекомендуется создавать настраиваемый элемент управления.
Связанная информация
Стили кодирования
См. Рекомендации по стилю кодирования 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. Создание файлов ресурсов подробно описывает, как это сделать.
Связанная информация
Разделение пользовательского интерфейса и логики
Одной из основных целей большинства разработчиков приложений является создание поддерживаемого приложения. Один из способов достижения этой цели — разделение пользовательского интерфейса и бизнес-логики. Ниже приведены несколько причин, почему пользовательский интерфейс приложения должен быть написан на 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.12
import QtQuick.Controls 2.12
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.12
import QtQuick.Controls 2.12
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 и Qt Quick, см. в разделе Инструменты и утилиты Qt Quick.
Граф сцены
Дополнительную информацию о графическом представлении сцены Qt Quick см. в разделе Графическое представление сцены Qt Quick.
Масштабируемые пользовательские интерфейсы
По мере улучшения разрешения дисплеев всё более важным становится масштабируемый пользовательский интерфейс приложения. Одним из подходов к этому является сохранение нескольких копий пользовательского интерфейса для разных разрешений экрана и загрузка соответствующей копии в зависимости от доступного разрешения. Хотя это работает довольно хорошо, это увеличивает нагрузку на сопровождение.
Qt предлагает лучшее решение этой проблемы и рекомендует разработчикам приложений следовать этим советам:
- Используйте якоря или модуль Qt Quick Layouts для размещения визуальных элементов.
- Не указывайте явную ширину и высоту для визуального элемента.
- Предоставляйте ресурсы пользовательского интерфейса, такие как изображения и значки, для каждой поддерживаемой вашим приложением разрешающей способности экрана. Пример галереи Qt Quick Controls 2 хорошо демонстрирует это, предоставляя
qt-logo.pngдля@2x,@3x, и@4xразрешений, что позволяет приложению адаптироваться к экранам высокой разрешающей способности. Qt автоматически выбирает подходящее изображение для данного дисплея, при условии, что функция масштабирования высокой DPI явно включена. - Используйте 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.12/qtquick-bestpractices.html