1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example itemviews/simpledommodel
\title Simple DOM Model Example
The Simple DOM Model example shows how an existing class can be adapted for use with
the model/view framework.
\image simpledommodel-example.png
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 around QDomDocument 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.
\section1 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 by QDomNode and its subclasses.
\omit
For example, the following code
snippet reads the contents of a file into a QDomDocument object and traverses the
document, reading all the plain text that can be found:
\snippet doc/src/snippets/code/doc_src_examples_simpledommodel.qdoc 0
In principle, the functions provided by QDomNode can be used to navigate from any
given starting point in a document to the piece of data requested by another component.
Since QDomDocument maintains information about the structure of a document, we can
use this to implement the required virtual functions in a QAbstractItemModel subclass.
\endomit
The aim is to use the structure provided by QDomDocument by wrapping QDomNode objects
in item objects similar to the \c TreeItem objects used in the
\l{Simple Tree Model Example}{Simple Tree Model} example.
\section1 DomModel Class Definition
Let us begin by examining the \c DomModel class:
\snippet examples/itemviews/simpledommodel/dommodel.h 0
The class definition contains all the basic functions that are needed for a
read-only model. Only the constructor and \c document() function are specific to
this model. The private \c domDocument variable is used to hold the document
that is exposed by the model; the \c rootItem variable contains a pointer to
the root item in the model.
\section1 DomItem Class Definition
The \c DomItem class is used to hold information about a specific QDomNode in
the document:
\snippet examples/itemviews/simpledommodel/domitem.h 0
Each \c DomItem provides a wrapper for a QDomNode 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 \c parent(), \c child(), and \c row() functions are convenience functions for
the \c DomModel to use that provide basic information about the item to be discovered
quickly. The node() function provides access to the underlying QDomNode 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.
\section1 DomItem Class Implementation
Since the \c DomItem class is only a thin wrapper around QDomNode 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:
\snippet examples/itemviews/simpledommodel/domitem.cpp 0
\snippet examples/itemviews/simpledommodel/domitem.cpp 1
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:
\snippet examples/itemviews/simpledommodel/domitem.cpp 4
\codeline
\snippet examples/itemviews/simpledommodel/domitem.cpp 6
\codeline
\snippet examples/itemviews/simpledommodel/domitem.cpp 3
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 \c child()
function to help create model indexes, and this constructs wrappers for the children
of the item's QDomNode, relating the row number of each child to the newly-constructed
wrapper:
\snippet examples/itemviews/simpledommodel/domitem.cpp 5
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:
\snippet examples/itemviews/simpledommodel/domitem.cpp 2
These, in turn, will delete their children and free any QDomNode objects in use.
\section1 DomModel Class Implementation
The structure provided by the \c DomItem class makes the implementation of \c DomModel
similar to the \c TreeModel shown in the
\l{Simple Tree Model Example}{Simple Tree Model} example.
The constructor accepts an existing document and a parent object for the model:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 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:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 1
All of the child items in the tree will be deleted by the \c DomItem destructor as
their parent items are deleted.
\section2 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 \c columnCount()
function:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 2
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 \c flags() function is straightforward
to write:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 5
Since the model is intended for use in a tree view, the \c headerData() function only
provides a horizontal header:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 6
The model presents the names of nodes in the first column, element attributes in the
second, and any node values in the third.
\section2 Navigating The Document
The index() function creates a model index for the item with the given row, column,
and parent in the model:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 7
The function first has to relate the parent index to an item that contains a node
from the underlying document. If the parent index is invalid, it refers to the root
node in the document, so we retrieve the root item that wraps it; otherwise, we
obtain a pointer to the relevant item using the QModelIndex::internalPointer()
function. We are able to extract a pointer in this way because any valid model index
will have been created by this function, and we store pointers to item objects in
any new indexes that we create with QAbstractItemModel::createIndex():
\snippet examples/itemviews/simpledommodel/dommodel.cpp 8
A child item for the given row is provided by the parent item's \c child() function.
If a suitable child item was found then we call
\l{QAbstractItemModel::createIndex()}{createIndex()} to produce a model index for the
requested row and column, passing a pointer to the child item for it to store
internally. If no suitable child item is found, an invalid model index is returned.
Note that the items themselves maintain ownership of their child items. This means
that the model does not need to keep track of the child items that have been created,
and can let the items themselves tidy up when they are deleted.
The number of rows beneath a given item in the model is returned by the \c rowCount()
function, and is the number of child nodes contained by the node that corresponds to
the specified model index:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 10
To obtain the relevant node in the underlying document, we access the item via the
internal pointer stored in the model index. If an invalid index is supplied, the
root item is used instead. We use the item's \c node() function to access the node
itself, and simply count the number of child nodes it contains.
Since the model is used to represent a hierarchical data structure, it needs to
provide an implementation for the \c parent() function. This returns a model index
that corresponds to the parent of a child model index supplied as its argument:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 9
For valid indexes other than the index corresponding to the root item, we obtain
a pointer to the relevant item using the method described in the \c index() function,
and use the item's \c parent() function to obtain a pointer to the parent item.
If no valid parent item exists, or if the parent item is the root item, we can simply
follow convention and return an invalid model index. For all other parent items, we
create a model index containing the appropriate row and column numbers, and a pointer
to the parent item we just obtained.
Data is provided by the \c data() function. For simplicity, we only provide data for
the \l{Qt::DisplayRole}{display role}, returning an invalid variant for all other
requests:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 3
As before, we obtain an item pointer for the index supplied, and use it to obtain
the underlying document node. Depending on the column specified, the data we return
is obtained in different ways:
\snippet examples/itemviews/simpledommodel/dommodel.cpp 4
For the first column, we return the node's name. For the second column, we read any
attributes that the node may have, and return a string that contains a space-separated
list of attribute-value assignments. For the third column, we return any value that
the node may have; this allows the contents of text nodes to be displayed in a view.
If data from any other column is requested, an invalid variant is returned.
\section1 Implementation Notes
Ideally, we would rely on the structure provided by QDomDocument to help us write
the \l{QAbstractItemModel::parent()}{parent()} and
\l{QAbstractItemModel::index()}{index()} functions that are required when subclassing
QAbstractItemModel. However, since Qt's DOM classes use their own system for
dynamically allocating memory for DOM nodes, we cannot guarantee that the QDomNode
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.
\omit
Since these items contain value references to the QDomNode objects themselves, this
has the side effect that the DOM nodes themselves can be used to reliably navigate
the document [not sure about this - QDom* may return different QDomNode objects for
the same piece of information]. However, this advantage is redundant since we need to
use wrapper items to obtain it. [Possible use of QDomNode cache in the model itself.]
\endomit
*/
|