Spec-Zone.ru › Qt 6.1

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

Spec-Zone.ru

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