Spec-Zone.ru › Qt 6.0

Динамическое создание 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-6.0/qtqml-javascript-dynamicobjectcreation.html

Spec-Zone.ru

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