Spec-Zone.ru › Qt 5.6

Динамическое создание 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-приложений, что навязывается на платформах, таких как iOS, 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/archives/qt-5.6/qtqml-javascript-dynamicobjectcreation.html

Spec-Zone.ru

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