Отслеживание динамических объектов
Работая с динамическими объектами, часто необходимо отслеживать созданные объекты. Еще одна распространенная задача – возможность сохранять и восстанавливать состояние динамических объектов. Обе эти задачи легко решаются с помощью модели 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 для их повторного создания. Это также требует, чтобы мы реализовали способ отличать динамически созданные элементы от элементов, которые являются частью исходной сцены, чтобы мы могли избежать попыток сериализовать, а затем десериализовать какие-либо из исходных элементов.