Динамическое создание QML-объектов из JavaScript
QML поддерживает динамическое создание объектов изнутри JavaScript. Это полезно для отсрочки инициализации объектов до необходимости, тем самым улучшая время запуска приложения. Это также позволяет визуальным объектам динамически создаваться и добавляться в сцену в ответ на пользовательский ввод или другие события.
См. пример Динамической сцены для демонстрации концепций, обсуждаемых на этой странице.
Динамическое создание объектов
Существует два способа динамического создания объектов из JavaScript. Вы можете вызвать Qt.createComponent() для динамического создания объекта Component или использовать Qt.createQmlObject() для создания объекта из строки QML. Создание компонента лучше, если у вас есть существующий компонент, определенный в документе QML, и вы хотите динамически создавать экземпляры этого компонента. В противном случае создание объекта из строки QML полезно, когда сам QML-объект генерируется во время выполнения.
Динамическое создание компонента
Чтобы динамически загрузить компонент, определенный в файле QML, вызовите функцию Qt.createComponent() в объекте Qt. Эта функция принимает в качестве единственного аргумента URL-адрес файла QML и создает объект Component из этого URL-адреса.
После получения объекта Component вы можете вызвать его метод createObject() для создания экземпляра компонента. Эта функция может принимать один или два аргумента:
- Первый — родительский объект для нового объекта. Родитель может быть графическим объектом (т. е. типа Item) или неграфическим объектом (т. е. типа QtObject или C++ QObject). Только графические объекты с графическими родительскими объектами будут отображаться на визуальной канве Qt Quick. Если вы хотите установить родителя позже, вы можете безопасно передать
nullв эту функцию. - Второй аргумент необязательный и представляет собой карту пар свойство-значение, которые определяют начальные значения любых свойств объекта. Значения свойств, указанные в этом аргументе, применяются к объекту до завершения его создания, что предотвращает ошибки привязки, которые могут возникнуть, если некоторые свойства должны быть инициализированы для включения других привязок свойств. Кроме того, есть небольшие преимущества в производительности по сравнению с определением значений свойств и привязок после создания объекта.
Вот пример. Сначала есть Sprite.qml, который определяет простой QML-компонент:
import QtQuick 2.0
Rectangle { width: 80; height: 50; color: "red" } Наш главный файл приложения, main.qml, импортирует файл JavaScript componentCreation.js, который будет создавать объекты Sprite:
import QtQuick 2.0
import "componentCreation.js" as MyScript
Rectangle {
id: appWindow
width: 300; height: 300
Component.onCompleted: MyScript.createSpriteObjects();
} Вот componentCreation.js. Обратите внимание, что он проверяет, является ли статус компонента status Component.Ready перед вызовом createObject() в случае, если файл QML загружается через сеть и поэтому не готов сразу.
var component;
var sprite;
function createSpriteObjects() {
component = Qt.createComponent("Sprite.qml");
if (component.status == Component.Ready)
finishCreation();
else
component.statusChanged.connect(finishCreation);
}
function finishCreation() {
if (component.status == Component.Ready) {
sprite = component.createObject(appWindow, {x: 100, y: 100});
if (sprite == null) {
// Error Handling
console.log("Error creating object");
}
} else if (component.status == Component.Error) {
// Error Handling
console.log("Error loading component:", component.errorString());
}
} Если вы уверены, что загружаемый файл QML является локальным, вы можете опустить функцию finishCreation() и вызвать createObject() сразу:
function createSpriteObjects() {
component = Qt.createComponent("Sprite.qml");
sprite = component.createObject(appWindow, {x: 100, y: 100});
if (sprite == null) {
// Error Handling
console.log("Error creating object");
}
} Обратите внимание, что в обоих случаях createObject() вызывается с appWindow в качестве родительского аргумента, так как динамически созданный объект является визуальным (Qt Quick) объектом. Созданный объект станет дочерним объектом объекта appWindow в main.qml, и отобразится на сцене.
При использовании файлов с относительными путями путь должен быть относительным к файлу, в котором выполняется Qt.createComponent().
Чтобы подключить сигналы к (или получать сигналы от) динамически созданных объектов, используйте метод сигнала connect(). Дополнительную информацию см. в разделе Подключение сигналов к методам и сигналам.
Также возможно создание экземпляров компонентов без блокировки с помощью функции incubateObject().
Создание объекта из строки QML
Если QML не определяется до выполнения, вы можете создать QML-объект из строки QML с помощью функции Qt.createQmlObject(), как показано в следующем примере:
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',
parentItem,
"dynamicSnippet1"); Первый аргумент — это строка QML для создания. Как и в новом файле, вам нужно будет импортировать любые типы, которые вы хотите использовать. Второй аргумент — родительский объект для нового объекта, а семантика аргумента родителя, которая применяется к компонентам, аналогична для createQmlObject(). Третий аргумент — путь к файлу, который нужно ассоциировать с новым объектом; он используется для отладки ошибок.
Если строка QML импортирует файлы с относительными путями, путь должен быть относительным к файлу, в котором определен родительский объект (второй аргумент метода).
Важно: При построении статических QML-приложений файлы QML сканируются для обнаружения зависимостей импорта. Таким образом, все необходимые плагины и ресурсы разрешаются во время компиляции. Однако учитываются только явные инструкции импорта (те, которые находятся в начале файла QML), а не инструкции импорта, заключенные в строковые литералы. Чтобы поддерживать статическое построение, вам необходимо убедиться, что файлы QML, использующие Qt.createQmlObject(), явно содержат все необходимые импорты в начале файла в дополнение к тем, которые находятся внутри строковых литералов.
Поддержка динамически созданных объектов
При управлении динамически созданными объектами необходимо убедиться, что контекст создания объекта остается активным. В противном случае, если контекст создания будет уничтожен раньше, привязки и обработчики сигналов в динамическом объекте больше работать не будут.
Фактический контекст создания зависит от того, как создается объект:
- Если используется Qt.createComponent(), контекстом создания является QQmlContext, в котором вызывается этот метод.
- Если вызывается Qt.createQmlObject(), контекстом создания является контекст родительского объекта, переданного в этот метод.
- Если объект
Component{}определен и вызывается createObject() или incubateObject() для этого объекта, контекстом создания является контекст, в котором определен объектComponent.
Также обратите внимание, что хотя динамически созданные объекты могут использоваться так же, как и другие объекты, у них нет идентификатора в QML.
Удаление объектов динамически
Во многих пользовательских интерфейсах достаточно установить непрозрачность визуального объекта в 0 или перенести визуальный объект за пределы экрана вместо удаления. Однако, если у вас много динамически созданных объектов, вы можете получить существенное улучшение производительности, если удалять неиспользуемые объекты.
Обратите внимание, что вы никогда не должны вручную удалять объекты, которые были динамически созданы удобными фабриками QML-объектов (такими как Loader и Repeater). Также следует избегать удаления объектов, которые вы не создавали сами.
Объекты могут быть удалены с помощью метода destroy(). Этот метод имеет необязательный аргумент (по умолчанию 0), который указывает приблизительную задержку в миллисекундах перед уничтожением объекта.
Вот пример. application.qml создает пять экземпляров компонента SelfDestroyingRect.qml. Каждый экземпляр выполняет NumberAnimation, а по окончании анимации вызывает destroy() на своем корневом объекте для уничтожения самого себя:
application.qml |
import QtQuick 2.0
Item {
id: container
width: 500; height: 100
Component.onCompleted: {
var component = Qt.createComponent("SelfDestroyingRect.qml");
for (var i=0; i<5; i++) {
var object = component.createObject(container);
object.x = (object.width + 10) * i;
}
}
} |
SelfDestroyingRect.qml |
import QtQuick 2.0
Rectangle {
id: rect
width: 80; height: 80
color: "red"
NumberAnimation on opacity {
to: 0
duration: 1000
onRunningChanged: {
if (!running) {
console.log("Destroying...")
rect.destroy();
}
}
}
} |
В качестве альтернативы, application.qml мог бы уничтожить созданный объект, вызвав object.destroy().
Обратите внимание, что безопасно вызывать destroy() для объекта внутри этого объекта. Объекты не уничтожаются мгновенно после вызова destroy(), а удаляются в какой-то момент между окончанием этого блока скрипта и следующим кадром (если вы не указали ненулевую задержку).
Также обратите внимание, что если экземпляр SelfDestroyingRect был создан статически, как в этом примере:
Item {
SelfDestroyingRect {
// ...
}
} Это приведет к ошибке, поскольку объекты можно уничтожать динамически только в том случае, если они были динамически созданы.
Объекты, созданные с помощью Qt.createQmlObject(), аналогично можно уничтожить с помощью destroy():
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',
parentItem,
"dynamicSnippet1");
newObject.destroy(1000);
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.14/qtqml-javascript-dynamicobjectcreation.html