Отслеживание динамических объектов

Добавлено 18 мая 2022 в 07:12

Работая с динамическими объектами, часто необходимо отслеживать созданные объекты. Еще одна распространенная задача – возможность сохранять и восстанавливать состояние динамических объектов. Обе эти задачи легко решаются с помощью модели XmlListModel, которая заполняется динамически.

В приведенном ниже примере пользователь может создавать и перемещать два типа элементов: ракеты (rocket) и НЛО (UFO). Чтобы иметь возможность манипулировать всей сценой из динамически созданных элементов, мы используем модель для отслеживания элементов.

Модель XmlListModel заполняется по мере создания элементов. Ссылка на объект отслеживается вместе с исходным URL, используемым при его создании. Последнее для отслеживания объектов не обязательно, но пригодится позже.

import QtQuick
import "create-object.js" as CreateObject

Item {
    id: root

    ListModel {
        id: objectsModel
    }

    function addUfo() {
        CreateObject.create("ufo.qml", root, itemAdded)
    }

    function addRocket() {
        CreateObject.create("rocket.qml", root, itemAdded)
    }

    function itemAdded(obj, source) {
        objectsModel.append({"obj": obj, "source": source})
    }

Как видно из приведенного выше примера, create-object.js – это более обобщенная форма скрипта JavaScript, представленного ранее в предыдущем разделе. Метод create использует три аргумента: исходный URL, корневой элемент и функцию обратного вызова, которую необходимо вызвать после завершения создания объекта. Функция обратного вызова вызывается с двумя аргументами: ссылкой на вновь созданный объект и используемым исходным URL.

Это означает, что каждый раз, когда вызываются функции addUfo или addRocket, функция itemAdded будет вызываться после создания нового объекта. Данная функция добавит ссылку на объект и URL источника в модель objectsModel.

objectsModel можно использовать по-разному. В рассматриваемом примере на нее опирается функция clearItems. Эта функция демонстрирует две вещи. Во-первых, как перебрать модель и выполнить задачу, то есть вызвать функцию destroy для каждого элемента, чтобы удалить его. Во-вторых, это подчеркивает тот факт, что модель не обновляется по мере уничтожения объектов. Вместо удаления элемента модели, связанного с рассматриваемым объектом, для свойства obj этого элемента модели устанавливается значение null. Чтобы исправить это, код должен явно очищать элемент модели при удалении объектов.

function clearItems() {
    while(objectsModel.count > 0) {
        objectsModel.get(0).obj.destroy()
        objectsModel.remove(0)
    }
}

Имея модель, представляющую все динамически созданные элементы, легко создать функцию, которая сериализует эти элементы. В этом примере кода сериализованная информация состоит из исходного URL каждого объекта с его свойствами x и y. Это свойства, которые могут быть изменены пользователем. Эта информация используется для формирования строки XML-документа.

function serialize() {
    var res = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<scene>\n"

    for(var ii=0; ii < objectsModel.count; ++ii) {
        var i = objectsModel.get(ii)
        res += "  <item>\n    <source>" + i.source + "</source>\n    <x>" + i.obj.x + "</x>\n    <y>" + i.obj.y + "</y>\n  </item>\n"
    }

    res += "</scene>"

    return res
}

Подсказка

В настоящее время XmlListModel в Qt 6 не имеет свойства xml и функции get(), необходимых для работы сериализации и десериализации.

Строку XML-документа можно использовать с XmlListModel, установив свойство xml модели. В приведенном ниже коде модель показана вместе с функцией deserialize. Функция deserialize запускает десериализацию, устанавливая dsIndex для ссылки на первый элемент модели, а затем вызывая создание этого элемента. Затем функция обратного вызова dsItemAdded устанавливает свойства x и y только что созданного объекта. Затем она обновляет индекс и создает следующий объект, если он есть.

XmlListModel {
    id: xmlModel
    query: "/scene/item"
    XmlListModelRole { name: "source"; elementName: "source" }
    XmlListModelRole { name: "x"; elementName: "x" }
    XmlListModelRole { name: "y"; elementName: "y" }
}

function deserialize() {
    dsIndex = 0
    CreateObject.create(xmlModel.get(dsIndex).source, root, dsItemAdded)
}

function dsItemAdded(obj, source) {
    itemAdded(obj, source)
    obj.x = xmlModel.get(dsIndex).x
    obj.y = xmlModel.get(dsIndex).y

    dsIndex++

    if (dsIndex < xmlModel.count) {
        CreateObject.create(xmlModel.get(dsIndex).source, root, dsItemAdded)
    }
}

property int dsIndex

В примере показано, как можно использовать модель для отслеживания созданных элементов и насколько просто сериализовать и десериализовать такую информацию. Это можно использовать для хранения динамически заполняемой сцены, такой как набор виджетов. В данном примере для отслеживания каждого элемента использовалась модель.

Альтернативным решением для отслеживания элементов может быть использование свойства children корня сцены. Это, однако, требует, чтобы сами элементы знали исходный URL для их повторного создания. Это также требует, чтобы мы реализовали способ отличать динамически созданные элементы от элементов, которые являются частью исходной сцены, чтобы мы могли избежать попыток сериализовать, а затем десериализовать какие-либо из исходных элементов.

Теги

GUI / Графический интерфейс пользователяJavaScriptQMLQtQtQuickПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.