Spec-Zone.ru › Qt 5.15

Динамическое создание 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-файлы сканируются для обнаружения зависимостей импорта. Таким образом, все необходимые плагины и ресурсы разрешаются во время компиляции. Однако учитываются только явные операторы import (те, которые находятся вверху QML-файла), а не операторы import, заключённые в строковые литералы. Чтобы поддерживать статические сборки, вам необходимо убедиться, что QML-файлы, использующие Qt.createQmlObject(), явным образом содержат все необходимые операторы import в начале файла, помимо тех, что находятся внутри строковых литералов.

Поддержка динамически созданных объектов

При управлении динамически созданными объектами необходимо убедиться, что контекст создания пережит созданным объектом. В противном случае, если контекст создания будет уничтожен первым, привязки и обработчики сигналов в динамическом объекте больше не будут работать.

Фактический контекст создания зависит от способа создания объекта:

  • Если используется 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.15/qtqml-javascript-dynamicobjectcreation.html

Spec-Zone.ru

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