source: trunk/doc/src/examples/qobjectxmlmodel.qdoc@ 1168

Last change on this file since 1168 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the documentation of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:FDL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in a
14** written agreement between you and Nokia.
15**
16** GNU Free Documentation License
17** Alternatively, this file may be used under the terms of the GNU Free
18** Documentation License version 1.3 as published by the Free Software
19** Foundation and appearing in the file included in the packaging of this
20** file.
21**
22** If you have questions regarding the use of this file, please contact
23** Nokia at [email protected].
24** $QT_END_LICENSE$
25**
26****************************************************************************/
27
28/*!
29 \example xmlpatterns/qobjectxmlmodel
30 \title QObject XML Model Example
31
32 This example shows how to use QtXmlPatterns to query QObject trees
33 by modeling the non-XML data structure of a QObject tree to look
34 like XML.
35
36 \tableofcontents
37
38 \section1 Introduction
39
40 This example illustrates two important points about using XQuery to
41 query non-XML data modeled to look like XML. The first point is that
42 a custom node model class doesn't always have to actually build the
43 node model. Sometimes the node model can be an already existing data
44 structure, like the QObject tree used in this example. The second
45 point is to explain what is required to make non-XML data look like
46 XML.
47
48 In this example, we want to model a QObject tree to look like
49 XML. That is easy to do because a QObject tree maps to the XML tree
50 structure in a staightforward way. Each QObject node is modeled as
51 an XML element node. However, when we want to add the QMetaObject tree
52 to the QObject tree node model, we are trying to add a second tree to
53 the node model. The QMetaObject tree exists \e{behind} the QObject
54 tree. Adding the QMetaObject tree to the node model changes the two
55 dimensional tree into a three dimensional tree.
56
57 The query engine can only traverse two dimensional trees, because an
58 XML document is always a two dimensional tree. If we want to add the
59 QMetaObject tree to the node model, we have to somehow flatten it
60 into the same plane as the QObject tree. This requires that the
61 node model class must build an auxiliary data structure and make it
62 part of the two dimensional QObject node model. How to do this is
63 explained in \l{Including The QMetaObject Tree}.
64
65 \section2 The User Interface
66
67 The UI for this example was created using Qt Designer:
68
69 \image qobjectxmlmodel-example.png
70
71 \section1 Code Walk-Through
72
73 The strategy for this example is different from the strategy for the
74 \l{File System Example}{file system example}. In the file system
75 example, the node model class had to actually build a node model
76 because the non-XML data to be traversed was the computer's file
77 system, a structure stored on disk in a form that the query engine
78 couldn't use. The node model class had to build an analog of the
79 computer's file system in memory.
80
81 For this example, the data structure to be traversed already exists
82 in memory in a usable form. It is the QObject tree of the example
83 application itself. All we need is the pointer to the root of the
84 QObject tree.
85
86 \note When we add the QMetaObject tree to the node model, the node
87 model class will have to build an auxiliary data structure to move
88 the QMetaObject tree into the same plane as the QObject tree. This
89 is explained later in \l{Including The QMetaObject Tree}.
90
91 \section2 The Custom Node Model Class: QObjextXmlModel
92
93 The node model class for this example is QObjextXmlModel, which is
94 derived from QSimpleXmlNodeModel. QObjextXmlModel implements the
95 callback interface functions that don't have implementations in
96 QSimpleXmlNodeModel:
97
98 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 0
99
100 The node model class declares three data members:
101
102 \target Three Data Members
103 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 2
104
105 The constructor sets \c m_baseURI to the QUrl constructed from the
106 \l{QCoreApplication::applicationFilePath()}{file path} of the
107 application executable. This is the value returned by
108 \l{QAbstractXmlNodeModel::documentUri()}{documentUri()}. The
109 constructor sets \c{m_root} to point to the QObject tree for the
110 example application. This is the node model that the query engine
111 will use. And the constructor calls a local function to build the
112 auxiliary data structure (\c{m_allMetaObjects}) for including the
113 QMetaObject tree in the node model. How this auxiliary data
114 structure is incorporated into the QObject node model is discussed
115 in \l{Including The QMetaObject Tree}.
116
117 \section3 Accessing The Node Model
118
119 Since the query engine knows nothing about QObject trees, it can
120 only access them by calling functions in the node model callback
121 interface. The query engine passes a QXmlNodeModelIndex to uniquely
122 identify a node in the node model. The QXmlNodeModelIndex is
123 constructed from a pointer to the QObject that represents the node.
124 \l{QAbstractXmlNodeModel::createIndex()}{createIndex()} creates the
125 QXmlNodeModelIndex, as in the local \c{root()} function, for example:
126
127 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 0
128
129 A QObject represents an element node in the node model, but we also
130 need to represent attribute nodes. For example, the class name of a
131 QObject is an attribute of the QObject, so it should be an attribute
132 node in the node model. A QObject's class name is obtained from the
133 QObject. (Actually, it is in the QMetaObject, which is obtained from
134 the QObject). This means that a single QObject logically represents
135 multiple nodes in the node model: the element node and potentially
136 many attribute nodes.
137
138 To uniquely identify an attribute node, we need the pointer to the
139 QObject containing the attribute, and an additional value that
140 identifies the attribute in the QObject. For this \e{additional
141 data} value, we use \c{enum QObjectNodeType}:
142
143 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 3
144
145 Ignore the \c{MetaObjectXXX} values for now. They will be explained
146 in \l{Including The QMetaObject Tree}. Here we are interested in the
147 three node types for QObject nodes: \c{IsQObject}, which represents
148 the element node type for a QObject, and \c{QObjectProperty} and
149 \c{QObjectClassName}, which represent the attribute node types for
150 the attributes of a QObject.
151
152 The \l{QAbstractXmlNodeModel::createIndex()}{createIndex()}
153 function called in the \c{root()} snippet above is the overload that
154 accepts a \c{void*} pointer and a second parameter,
155 \c{additionalData}, with default value 0 (\c{IsQObject}). Wherever
156 you see a call to \l{QAbstractXmlNodeModel::createIndex()}
157 {createIndex()} that only passes the QObject pointer, it is creating
158 the node index for a QObject element node. To create the node index
159 for the class name attribute, for example, the \l{QObject
160 attributes} {attributes()} function uses
161 \c{createIndex(object,QObjectClassName)}.
162
163 \target QObject attributes
164 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 6
165 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 8
166
167 \l{QObject attributes} {attributes()} is one of the callback
168 functions you have to implement in your custom node model class. It
169 returns a QVector of \l{QXmlNodeModelIndex} {node indexes} for all
170 the attribute nodes for QObject \c{n}. It calls
171 \l{QAbstractXmlNodeModel::createIndex()} {createIndex()} in two places.
172 Both calls use the QObject pointer from the current node \c{n} (the
173 element node), and just add a different value for the \e{additional data}
174 parameter. This makes sense because, in XML, the attributes of an
175 element are part of that element.
176
177 \section3 Traversing The Node Model
178
179 The query engine traverses the QObject tree by calling back to the
180 node model class's implementation of \l{QObject nextFromSimpleAxis}
181 {nextFromSimpleAxis()}. This function is the heart of the callback
182 interface, and it will probably be the most complex to implement in
183 your custom node model class. Below is a partial listing of the
184 implementation for this example. The full listing will be shown in
185 \l{Including The QMetaObject Tree}, where we discuss traversing the
186 QMetaObject tree.
187
188 \target QObject nextFromSimpleAxis
189 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 2
190 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 4
191
192 The main switch uses \c toNodeType(), which obtains the node
193 type from \l{QXmlNodeModelIndex::additionalData()}:
194
195 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 1
196
197 \c{case IsObject} case is the most interesting. It switches again on
198 the value of the \c{axis} parameter, which specifies the direction
199 the query engine wants to take from the current node. It is one of
200 the four enum values of \l{QAbstractXmlNodeModel::SimpleAxis}. The
201 \l{QAbstractXmlNodeModel::Parent} {Parent} and
202 \l{QAbstractXmlNodeModel::FirstChild} {FirstChild} cases reduce to
203 calls to QObject::parent() and QObject::children()
204 respectively. Note that a default constructed QXmlNodeModelIndex is
205 returned in the \l{QAbstractXmlNodeModel::Parent} {Parent} case if
206 the current node is the root, and in the
207 \l{QAbstractXmlNodeModel::FirstChild} {FirstChild} case if the
208 current node has no children.
209
210 For the \l{QAbstractXmlNodeModel::NextSibling} {NextSibling} and
211 \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} axes,
212 the helper function \c{qObjectSibling()} is called, with +1 to
213 traverse to the \l{QAbstractXmlNodeModel::NextSibling} {NextSibling}
214 and -1 to traverse to the
215 \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling}.
216
217 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 5
218
219 \c{qObjectSibling()} determines whether or not the node has any
220 siblings. It is called with \c{n}, the index of the current node.
221 If the current node is a child, then it has a parent with children
222 (the current node one of these).
223 So, we get the \l{QObject::parent()}{parent}, obtain the parent's
224 \l{QObject::children()} {child list}, find the current node in the
225 list, and construct the node index for the next or previous child
226 (sibling) and return it.
227
228 \note In \l{QObject nextFromSimpleAxis} {nextFromSimpleAxis()}, the
229 special case of asking for the
230 \l{QAbstractXmlNodeModel::PreviousSibling} {PreviousSibling} of the
231 root node is discussed in \l{Including The QMetaObject Tree}.
232
233 Traversing away from a \c{QObjectClassName} attribute node or a
234 \c{QObjectProperty} attribute node might seem a bit confusing at
235 first glance. The only move allowed from an attribute node is to the
236 \l{QAbstractXmlNodeModel::Parent} {Parent}, because attribute nodes
237 don't have children. But these two cases simply return the
238 \l{QXmlNodeModelIndex} {node index} of the current node.
239
240 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 7
241
242 Since \c n is the QXmlNodeModelIndex of the current node, all this
243 does is create another QXmlNodeModelIndex for the current node and
244 return it. This was explained above in \l{Accessing The Node Model},
245 where we saw that each QObject in the node model actually represents
246 an element node and potentially many attribute nodes. Traversing to
247 the parent node of an attribute simply creates a node index for the
248 same QObject, but with an \e{additional data} value of 0
249 (\c{IsQObject}).
250
251 If we only wanted to traverse the QObject tree with XQuery, we could
252 just implement the rest of the virtual callback functions listed
253 earlier and we would be done. The implementations for the remaining
254 functions are straightforward. But if we also want to use XQuery to
255 traverse the QMetaObject tree, we must include the QMetaObject tree
256 in the custom node model.
257
258 \section3 Including The QMetaObject Tree
259
260 The \l{Meta-Object System} {metaobject system} not only enables Qt's
261 \l{Signals and Slots} {signals and slots}, it also provides type
262 information that is useful at run-time; e.g., getting and setting
263 properties without knowing the property names at compile time. Each
264 QObject has an associated QMetaObject tree which contains all this
265 useful type information. Given a QObject, its QMetaObject is
266 obtained with QObject::metaObject(). Then QMetaObject::superClass()
267 can be called repeatedly to get the QMetaObject for each class in the
268 class hierarchy for the original QObject.
269
270 However, the QMetaObject hierarchy is a second tree in a plan that
271 exists logically behind the plane of the QObject tree. The QtXmlPatterns
272 query engine can only traverse a two dimensional node model that
273 represents an XML tree. If we want to include the QMetaObject in the
274 same node model that represents the QObject tree, we must find a way
275 to flatten the QMetaObject tree into the same plane as the QObject
276 tree.
277
278 The node model class declares \l{All MetaObjects}{m_allMetaObjects}
279 as a vector of pointers to QMetaObject:
280
281 \target All MetaObjects
282 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 1
283 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.h 4
284
285 This vector gets populated by the QObjectXmlModel constructor by
286 calling the private allMetaObjects() function:
287
288 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 9
289
290 The first half of the function is an example of the standard code
291 pattern for using QtXmlPatterns to run an XQuery. First it creates an
292 instance of QXmlQuery. Then it \l{QXmlQuery::bindVariable()}{binds}
293 the XQuery variable \c{$root} to the root node of the of the node
294 model; i.e., the root of the QObject tree. Then it
295 \l{QXmlQuery::setQuery()} {sets the query} to be an XQuery that
296 returns all the QObjects in the node model. Finally, the query is
297 evaluated into a \l{QXmlResultItems} {result item list}.
298
299 \note \l{QXmlQuery::bindVariable()} must be called before
300 \l{QXmlQuery::setQuery()}, because setting the query causes
301 QtXmlPatterns to \e compile the XQuery, which requires knowledge of
302 the variable bindings.
303
304 The second half of the function traverses the \l{QXmlResultItems}
305 {result item list}, getting the QMetaObject hierarchy for each
306 QObject and appending it to \l{All MetaObjects} {m_allMetaObjects},
307 if it isn't already there. But how do we include this vector of
308 pointers to QMetaObjects in the node model? The key insight is
309 shown in the full listing of \l{Full Listing of nextFromSimpleAxis}
310 {nextFromSimpleAxis()}, where we are interested now in the
311 \c{MetaObjectXXX} cases:
312
313 \target Full Listing of nextFromSimpleAxis
314 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 2
315 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 3
316 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 4
317
318 But first, revisit the \c{PreviousSibling} case for the
319 \c{IsQObject} case:
320
321 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 10
322
323 When asking for the previous sibling of the root of the QObject
324 tree, it creates a node model index with a null QObject pointer and
325 an \c{additionalData} value of \c{MetaObjects}. This effectively
326 allows the query engine to jump from the QObject tree to the
327 QMetaObject tree.
328
329 The query engine can jump from the QMetaObject tree back to the
330 QObject tree in the \c{NextSibling} case of case \c{MetaObjects},
331 where the \c{root()} function is called:
332
333 \snippet examples/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp 11
334
335 Having jumped from the QObject tree to the QMetaObject tree, the
336 query engine will use the \c{MetaObject}, \c{MetaObjectClassName},
337 and \c{MetaObjectSuperClass} cases, which are similar to the cases
338 for \c{IsQObject}, \c{QObjectProperty}, and \c{QObjectClassName}.
339*/
Note: See TracBrowser for help on using the repository browser.