Simple DOM Model Example¶
The Simple DOM Model example shows how an existing class can be adapted for use with the model/view framework.
Qt provides two complementary sets of classes for reading XML files: The classes based around
QXmlReader
provide a SAX-style API for incremental reading of large files, and the classes based aroundQDomDocument
enable developers to access the contents of XML files using a Document Object Model (DOM) API.In this example, we create a model that uses the DOM API to expose the structure and contents of XML documents to views via the standard QAbstractModel interface.
Design and Concepts¶
Reading an XML document with Qt’s DOM classes is a straightforward process. Typically, the contents of a file are supplied to
QDomDocument
, and nodes are accessed using the functions provided byQDomNode
and its subclasses.The aim is to use the structure provided by
QDomDocument
by wrappingQDomNode
objects in item objects similar to theTreeItem
objects used in the Simple Tree Model example.
DomModel Class Definition¶
Let us begin by examining the
DomModel
class:class DomModel : public QAbstractItemModel { Q_OBJECT public: explicit DomModel(const QDomDocument &document, QObject *parent = nullptr); ~DomModel(); QVariant data(const QModelIndex &index, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; private: QDomDocument domDocument; DomItem *rootItem; };The class definition contains all the basic functions that are needed for a read-only model. Only the constructor and
document()
function are specific to this model. The privatedomDocument
variable is used to hold the document that is exposed by the model; therootItem
variable contains a pointer to the root item in the model.
DomItem Class Definition¶
The
DomItem
class is used to hold information about a specificQDomNode
in the document:class DomItem { public: DomItem(const QDomNode &node, int row, DomItem *parent = nullptr); ~DomItem(); DomItem *child(int i); DomItem *parent(); QDomNode node() const; int row() const; private: QDomNode domNode; QHash<int, DomItem *> childItems; DomItem *parentItem; int rowNumber; };Each
DomItem
provides a wrapper for aQDomNode
obtained from the underlying document which contains a reference to the node, it’s location in the parent node’s list of child nodes, and a pointer to a parent wrapper item.The
parent()
,child()
, androw()
functions are convenience functions for theDomModel
to use that provide basic information about the item to be discovered quickly. The node() function provides access to the underlyingQDomNode
object.As well as the information supplied in the constructor, the class maintains a cache of information about any child items. This is used to provide a collection of persistent item objects that the model can identify consistently and improve the performance of the model when accessing child items.
DomItem Class Implementation¶
Since the
DomItem
class is only a thin wrapper aroundQDomNode
objects, with a few additional features to help improve performance and memory usage, we can provide a brief outline of the class before discussing the model itself.The constructor simply records details of the
QDomNode
that needs to be wrapped:DomItem::DomItem(const QDomNode &node, int row, DomItem *parent) : domNode(node), parentItem(parent), rowNumber(row) {}As a result, functions to provide the parent wrapper, the row number occupied by the item in its parent’s list of children, and the underlying
QDomNode
for each item are straightforward to write:DomItem *DomItem::parent() { return parentItem; } int DomItem::row() const { return rowNumber; } QDomNode DomItem::node() const { return domNode; }It is necessary to maintain a collection of items which can be consistently identified by the model. For that reason, we maintain a hash of child wrapper items that, to minimize memory usage, is initially empty. The model uses the item’s
child()
function to help create model indexes, and this constructs wrappers for the children of the item’sQDomNode
, relating the row number of each child to the newly-constructed wrapper:DomItem *DomItem::child(int i) { DomItem *childItem = childItems.value(i); if (childItem) return childItem; // if child does not yet exist, create it if (i >= 0 && i < domNode.childNodes().count()) { QDomNode childNode = domNode.childNodes().item(i); childItem = new DomItem(childNode, i, this); childItems[i] = childItem; } return childItem; }If a
QDomNode
was previously wrapped, the cached wrapper is returned; otherwise, a new wrapper is constructed and stored for valid children, and zero is returned for invalid ones.The class’s destructor deletes all the child items of the wrapper:
DomItem::~DomItem() { qDeleteAll(childItems); }These, in turn, will delete their children and free any
QDomNode
objects in use.
DomModel Class Implementation¶
The structure provided by the
DomItem
class makes the implementation ofDomModel
similar to theTreeModel
shown in the Simple Tree Model example.The constructor accepts an existing document and a parent object for the model:
DomModel::DomModel(const QDomDocument &document, QObject *parent) : QAbstractItemModel(parent), domDocument(document), rootItem(new DomItem(domDocument, 0)) { }A shallow copy of the document is stored for future reference, and a root item is created to provide a wrapper around the document. We assign the root item a row number of zero only to be consistent since the root item will have no siblings.
Since the model only contains information about the root item, the destructor only needs to delete this one item:
DomModel::~DomModel() { delete rootItem; }All of the child items in the tree will be deleted by the
DomItem
destructor as their parent items are deleted.
Basic Properties of The Model¶
Some aspects of the model do not depend on the structure of the underlying document, and these are simple to implement.
The number of columns exposed by the model is returned by the
columnCount()
function:int DomModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3; }This value is fixed, and does not depend on the location or type of the underlying node in the document. We will use these three columns to display different kinds of data from the underlying document.
Since we only implement a read-only model, the
flags()
function is straightforward to write:Qt::ItemFlags DomModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; return QAbstractItemModel::flags(index); }Since the model is intended for use in a tree view, the
headerData()
function only provides a horizontal header:QVariant DomModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Name"); case 1: return tr("Attributes"); case 2: return tr("Value"); default: break; } } return QVariant(); }The model presents the names of nodes in the first column, element attributes in the second, and any node values in the third.
Implementation Notes¶
Ideally, we would rely on the structure provided by
QDomDocument
to help us write theparent()
andindex()
functions that are required when subclassingQAbstractItemModel
. However, since Qt’s DOM classes use their own system for dynamically allocating memory for DOM nodes, we cannot guarantee that theQDomNode
objects returned for a given piece of information will be the same for subsequent accesses to the document.We use item wrappers for each
QDomNode
to provide consistent pointers that the model can use to navigate the document structure.
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.