Динамическое создание 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, импортирует файл componentCreation.js JavaScript, который будет создавать 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(), как показано в следующем примере:
const newObject = Qt.createQmlObject(`
import QtQuick 2.0
Rectangle {
color: "red"
width: 20
height: 20
}
`,
parentItem,
"myDynamicSnippet"
); Первый аргумент — это строка QML для создания. Так же, как и в новом файле, вам нужно будет импортировать любые типы, которые вы хотите использовать. Второй аргумент — это родительский объект для нового объекта, и семантика аргумента родителя, которая применяется к компонентам, аналогичным образом применима к createQmlObject(). Третий аргумент — это путь к файлу, который необходимо связать с новым объектом; он используется для отслеживания ошибок.
Если строка QML импортирует файлы с помощью относительных путей, путь должен быть относительным к файлу, в котором определён родительский объект (второй аргумент метода).
Важно: При построении статических QML-приложений QML-файлы сканируются для обнаружения зависимостей импорта. Таким образом, все необходимые плагины и ресурсы разрешаются во время компиляции. Однако учитываются только явные операторы импорта (те, которые находятся вверху файла QML), а не операторы импорта, заключённые в строковые литералы. Для поддержки статической сборки необходимо убедиться, что QML-файлы, использующие Qt.createQmlObject(), явно содержат все необходимые импорты в верхней части файла, помимо тех, что внутри строковых литералов.
Поддержание динамически созданных объектов
При управлении динамически созданными объектами необходимо убедиться, что контекст создания существует дольше, чем созданный объект. В противном случае, если контекст создания будет уничтожен первым, привязки и обработчики сигналов в динамическом объекте больше не будут работать.
Фактический контекст создания зависит от способа создания объекта:
- Если используется Qt.createComponent(), контекст создания — это QQmlContext, в котором вызывается этот метод
- Если вызван Qt.createQmlObject(), контекст создания — это контекст родительского объекта, переданного в этот метод
- Если объект
Component{}определён, и createObject() или incubateObject() вызываются на этом объекте, контекст создания — это контекст, в котором определёнComponent
Также обратите внимание, что, хотя динамически созданные объекты могут использоваться так же, как и другие объекты, они не имеют id в 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():
const newObject = Qt.createQmlObject(`
import QtQuick 2.0
Rectangle {
color: "red"
width: 20
height: 20
}
`,
parentItem,
"myDynamicSnippet"
);
newObject.destroy(1000);
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtqml-javascript-dynamicobjectcreation.html