~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to doc/html/itemviews-simpletreemodel.html

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="iso-8859-1"?>
 
2
<!DOCTYPE html
 
3
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
 
4
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 
5
<!-- /tmp/qt-4.0.0-espenr-1119621036935/qt-x11-opensource-desktop-4.0.0/doc/src/examples/simpletreemodel.qdoc -->
 
6
<head>
 
7
    <title>Qt 4.0: Simple Tree Model</title>
 
8
    <style>h3.fn,span.fn { margin-left: 1cm; text-indent: -1cm; }
 
9
a:link { color: #004faf; text-decoration: none }
 
10
a:visited { color: #672967; text-decoration: none }
 
11
td.postheader { font-family: sans-serif }
 
12
tr.address { font-family: sans-serif }
 
13
body { background: #ffffff; color: black; }</style>
 
14
</head>
 
15
<body>
 
16
<table border="0" cellpadding="0" cellspacing="0" width="100%">
 
17
<tr>
 
18
<td align="left" valign="top" width="32"><img src="images/qt-logo.png" align="left" width="32" height="32" border="0" /></td>
 
19
<td width="1">&nbsp;&nbsp;</td><td class="postheader" valign="center"><a href="index.html"><font color="#004faf">Home</font></a>&nbsp;&middot; <a href="classes.html"><font color="#004faf">All&nbsp;Classes</font></a>&nbsp;&middot; <a href="mainclasses.html"><font color="#004faf">Main&nbsp;Classes</font></a>&nbsp;&middot; <a href="annotated.html"><font color="#004faf">Annotated</font></a>&nbsp;&middot; <a href="groups.html"><font color="#004faf">Grouped&nbsp;Classes</font></a>&nbsp;&middot; <a href="functions.html"><font color="#004faf">Functions</font></a></td>
 
20
<td align="right" valign="top" width="230"><img src="images/trolltech-logo.png" align="right" width="203" height="32" border="0" /></td></tr></table><h1 align="center">Simple Tree Model</h1>
 
21
<p>Files:</p>
 
22
<ul>
 
23
<li><a href="itemviews-simpletreemodel-treeitem-cpp.html">itemviews/simpletreemodel/treeitem.cpp</a></li>
 
24
<li><a href="itemviews-simpletreemodel-treeitem-h.html">itemviews/simpletreemodel/treeitem.h</a></li>
 
25
<li><a href="itemviews-simpletreemodel-treemodel-cpp.html">itemviews/simpletreemodel/treemodel.cpp</a></li>
 
26
<li><a href="itemviews-simpletreemodel-treemodel-h.html">itemviews/simpletreemodel/treemodel.h</a></li>
 
27
<li><a href="itemviews-simpletreemodel-main-cpp.html">itemviews/simpletreemodel/main.cpp</a></li>
 
28
<li><a href="itemviews-simpletreemodel-simpletreemodel-qrc.html">itemviews/simpletreemodel/simpletreemodel.qrc</a></li>
 
29
</ul>
 
30
<p>The Simple Tree Model example shows how to create a basic, read-only hierarchical model to use with Qt's standard view classes. For a description of simple non-hierarchical list and table models, see the <a href="model-view-programming.html">Model/View Programming</a> overview.</p>
 
31
<center><img src="images/simpletreemodel-example.png" /></center><p>Qt's model/view architecture provides a standard way for views to manipulate information in a data source, using an abstract model of the data to simplify and standardize the way it is accessed. Simple models represent data as a table of items, and allow views to access this data via an <a href="model-view-model.html">index-based</a> system. More generally, models can be used to represent data in the form of a tree structure by allowing each item to act as a parent to a table of child items.</p>
 
32
<p>Before attempting to implement a tree model, it is worth considering whether the data is supplied by an external source, or whether it is going to be maintained within the model itself. In this example, we will implement an internal structure to hold data rather than discuss how to package data from an external source.</p>
 
33
<a name="design-and-concepts"></a>
 
34
<h2>Design and Concepts</h2>
 
35
<p>The data structure that we use to represent the structure of the data takes the form of a tree built from <tt>TreeItem</tt> objects. Each <tt>TreeItem</tt> represents an item in a tree view, and contains several columns of data.</p>
 
36
<a name="simpletreemodelstructure"></a><table align="center" cellpadding="2" cellspacing="1" border="0">
 
37
<tr valign="top" bgcolor="#f0f0f0"><td><img src="images/treemodel-structure.png" /></td><td><b>Simple Tree Model Structure</b><p>The data is stored internally in the model using <tt>TreeItem</tt> objects that are linked together in a pointer-based tree structure. Generally, each <tt>TreeItem</tt> has a parent item, and can have a number of child items. However, the root item in the tree structure has no parent item; this item is never referenced outside the model.</p>
 
38
<p>Each <tt>TreeItem</tt> contains information about its place in the tree structure; it can return its parent item and its row number. Having this information readily available makes implementing the model easier.</p>
 
39
<p>Since each item in a tree view usually contains several columns of data (a title and a summary in this example), it is natural to store this information in each item. For simplicity, we will use a list of <a href="qvariant.html">QVariant</a> objects to store the data for each column in the item.</p>
 
40
</td></tr>
 
41
</table>
 
42
<p>The use of a pointer-based tree structure means that, when passing a model index to a view, we can use record the address of the corresponding item in the index (see <a href="qabstractitemmodel.html#createIndex">QAbstractItemModel::createIndex</a>()) and retrieve it later with <a href="qmodelindex.html#internalPointer">QModelIndex::internalPointer</a>(). This makes writing the model easier and ensures that all model indexes that refer to the same item have the same internal data pointer.</p>
 
43
<p>With the appropriate data structure in place, we can create a tree model with a minimal amount of extra code to supply model indexes and data to other components.</p>
 
44
<a name="treeitem-class-definition"></a>
 
45
<h2>TreeItem Class Definition</h2>
 
46
<p>The <tt>TreeItem</tt> class is defined as follows:</p>
 
47
<pre>&nbsp;   class TreeItem
 
48
    {
 
49
    public:
 
50
        TreeItem(const QList&lt;QVariant&gt; &amp;data, TreeItem *parent = 0);
 
51
        ~TreeItem();
 
52
 
 
53
        void appendChild(TreeItem *child);
 
54
 
 
55
        TreeItem *child(int row);
 
56
        int childCount() const;
 
57
        int columnCount() const;
 
58
        QVariant data(int column) const;
 
59
        int row() const;
 
60
        TreeItem *parent();
 
61
 
 
62
    private:
 
63
        QList&lt;TreeItem*&gt; childItems;
 
64
        QList&lt;QVariant&gt; itemData;
 
65
        TreeItem *parentItem;
 
66
    };</pre>
 
67
<p>The class is a basic C++ class. It does not inherit from <a href="qobject.html">QObject</a> or provide signals and slots. It is used to hold a list of QVariants, containing column data, and information about its position in the tree structure. The functions provide the following features:</p>
 
68
<ul>
 
69
<li>The <tt>appendChildItem()</tt> is used to add data when the model is first constructed and is not used during normal use.</li>
 
70
<li>The <tt>child()</tt> and <tt>childCount()</tt> functions allow the model to obtain information about any child items.</li>
 
71
<li>Information about the number of columns associated with the item is provided by <tt>columnCount()</tt>, and the data in each column can be obtained with the data() function.</li>
 
72
<li>The <tt>row()</tt> and <tt>parent()</tt> functions are used to obtain the item's row number and parent item.</li>
 
73
</ul>
 
74
<p>The parent item and column data are stored in the <tt>parentItem</tt> and <tt>itemData</tt> private member variables. The <tt>childItems</tt> variable contains a list of pointers to the item's own child items.</p>
 
75
<a name="treeitem-class-implementation"></a>
 
76
<h2>TreeItem Class Implementation</h2>
 
77
<p>The constructor is only used to record the item's parent and the data associated with each column.</p>
 
78
<pre>&nbsp;   TreeItem::TreeItem(const QList&lt;QVariant&gt; &amp;data, TreeItem *parent)
 
79
    {
 
80
        parentItem = parent;
 
81
        itemData = data;
 
82
    }</pre>
 
83
<p>A pointer to each of the child items belonging to this item will be stored in the <tt>childItems</tt> private member variable. When the class's destructor is called, it must delete each of these to ensure that their memory is reused:</p>
 
84
<pre>&nbsp;   TreeItem::~TreeItem()
 
85
    {
 
86
        qDeleteAll(childItems);
 
87
    }</pre>
 
88
<p>Since each of the child items are constructed when the model is initially populated with data, the function to add child items is straightforward:</p>
 
89
<pre>&nbsp;   void TreeItem::appendChild(TreeItem *item)
 
90
    {
 
91
        childItems.append(item);
 
92
    }</pre>
 
93
<p>Each item is able to return any of its child items when given a suitable row number. For example, in the <a href="#simpletreemodelstructure">above diagram</a>, the item marked with the letter &quot;A&quot; corresponds to the child of the root item with <tt>row = 0</tt>, the &quot;B&quot; item is a child of the &quot;A&quot; item with <tt>row = 1</tt>, and the &quot;C&quot; item is a child of the root item with <tt>row = 1</tt>.</p>
 
94
<p>The <tt>child()</tt> function returns the child that corresponds to the specified row number in the item's list of child items:</p>
 
95
<pre>&nbsp;   TreeItem *TreeItem::child(int row)
 
96
    {
 
97
        return childItems.value(row);
 
98
    }</pre>
 
99
<p>The number of child items held can be found with <tt>childCount()</tt>:</p>
 
100
<pre>&nbsp;   int TreeItem::childCount() const
 
101
    {
 
102
        return childItems.count();
 
103
    }</pre>
 
104
<p>The <tt>TreeModel</tt> uses this function to determine the number of rows that exist for a given parent item.</p>
 
105
<p>The <tt>row()</tt> function reports the item's location within its parent's list of items:</p>
 
106
<pre>&nbsp;   int TreeItem::row() const
 
107
    {
 
108
        if (parentItem)
 
109
            return parentItem-&gt;childItems.indexOf(const_cast&lt;TreeItem*&gt;(this));
 
110
 
 
111
        return 0;
 
112
    }</pre>
 
113
<p>Note that, although the root item (with no parent item) is automatically assigned a row number of 0, this information is never used by the model.</p>
 
114
<p>The number of columns of data in the item is trivially returned by the <tt>columnCount()</tt> function.</p>
 
115
<pre>&nbsp;   int TreeItem::columnCount() const
 
116
    {
 
117
        return itemData.count();
 
118
    }</pre>
 
119
<p>Column data is returned by the <tt>data()</tt> function, taking advantage of <a href="qlist.html">QList</a>'s ability to provide sensible default values if the column number is out of range:</p>
 
120
<pre>&nbsp;   QVariant TreeItem::data(int column) const
 
121
    {
 
122
        return itemData.value(column);
 
123
    }</pre>
 
124
<p>The item's parent is found with <tt>parent()</tt>:</p>
 
125
<pre>&nbsp;   TreeItem *TreeItem::parent()
 
126
    {
 
127
        return parentItem;
 
128
    }</pre>
 
129
<p>Note that, since the root item in the model will not have a parent, this function will return zero in that case. We need to ensure that the model handles this case correctly when we implement the <tt>TreeModel::parent()</tt> function.</p>
 
130
<a name="treemodel-class-definition"></a>
 
131
<h2>TreeModel Class Definition</h2>
 
132
<p>The <tt>TreeModel</tt> class is defined as follows:</p>
 
133
<pre>&nbsp;   class TreeModel : public QAbstractItemModel
 
134
    {
 
135
        Q_OBJECT
 
136
 
 
137
    public:
 
138
        TreeModel(const QString &amp;data, QObject *parent = 0);
 
139
        ~TreeModel();
 
140
 
 
141
        QVariant data(const QModelIndex &amp;index, int role) const;
 
142
        Qt::ItemFlags flags(const QModelIndex &amp;index) const;
 
143
        QVariant headerData(int section, Qt::Orientation orientation,
 
144
                            int role = Qt::DisplayRole) const;
 
145
        QModelIndex index(int row, int column,
 
146
                          const QModelIndex &amp;parent = QModelIndex()) const;
 
147
        QModelIndex parent(const QModelIndex &amp;index) const;
 
148
        int rowCount(const QModelIndex &amp;parent = QModelIndex()) const;
 
149
        int columnCount(const QModelIndex &amp;parent = QModelIndex()) const;
 
150
 
 
151
    private:
 
152
        void setupModelData(const QStringList &amp;lines, TreeItem *parent);
 
153
 
 
154
        TreeItem *rootItem;
 
155
    };</pre>
 
156
<p>This class is similar to most other subclasses of <a href="qabstractitemmodel.html">QAbstractItemModel</a> that provide read-only models. Only the form of the constructor and the <tt>setupModelData()</tt> function are specific to this model. In addition, we provide a destructor to clean up when the model is destroyed.</p>
 
157
<a name="treemodel-class-implementation"></a>
 
158
<h2>TreeModel Class Implementation</h2>
 
159
<p>For simplicity, the model does not allow its data to be edited. As a result, the constructor takes an argument containing the data that the model will share with views and delegates:</p>
 
160
<pre>&nbsp;   TreeModel::TreeModel(const QString &amp;data, QObject *parent)
 
161
        : QAbstractItemModel(parent)
 
162
    {
 
163
        QList&lt;QVariant&gt; rootData;
 
164
        rootData &lt;&lt; &quot;Title&quot; &lt;&lt; &quot;Summary&quot;;
 
165
        rootItem = new TreeItem(rootData);
 
166
        setupModelData(data.split(QString(&quot;\n&quot;)), rootItem);
 
167
    }</pre>
 
168
<p>It is up to the constructor to create a root item for the model. This item only contains vertical header data for convenience. We also use it to reference the internal data structure that contains the model data, and it is used to represent an imaginary parent of top-level items in the model.</p>
 
169
<p>The model's internal data structure is populated with items by the <tt>setupModelData()</tt> function. We will examine this function separately at the end of this document.</p>
 
170
<p>The destructor ensures that the root item and all of its descendants are deleted when the model is destroyed:</p>
 
171
<pre>&nbsp;   TreeModel::~TreeModel()
 
172
    {
 
173
        delete rootItem;
 
174
    }</pre>
 
175
<p>Since we cannot add data to the model after it is constructed and set up, this simplifies the way that the internal tree of items is managed.</p>
 
176
<p>Models must implement an <tt>index()</tt> function to provide indexes for views and delegates to use when accessing data. Indexes are created for other components when they are referenced by their row and column numbers, and their parent model index. If an invalid model index is specified as the parent, it is up to the model to return an index that corresponds to a top-level item in the model.</p>
 
177
<p>When supplied with a model index, we first check whether it is valid. If it is not, we assume that a top-level item is being referred to; otherwise, we obtain the data pointer from the model index with its <a href="qmodelindex.html#internalPointer">internalPointer()</a> function and use it to reference a <tt>TreeItem</tt> object. Note that all the model indexes that we construct will contain a pointer to an existing <tt>TreeItem</tt>, so we can guarantee that any valid model indexes that we receive will contain a valid data pointer.</p>
 
178
<pre>&nbsp;   QModelIndex TreeModel::index(int row, int column, const QModelIndex &amp;parent)
 
179
                const
 
180
    {
 
181
        TreeItem *parentItem;
 
182
 
 
183
        if (!parent.isValid())
 
184
            parentItem = rootItem;
 
185
        else
 
186
            parentItem = static_cast&lt;TreeItem*&gt;(parent.internalPointer());
 
187
 
 
188
        TreeItem *childItem = parentItem-&gt;child(row);
 
189
        if (childItem)
 
190
            return createIndex(row, column, childItem);
 
191
        else
 
192
            return QModelIndex();
 
193
    }</pre>
 
194
<p>Since the row and column arguments to this function refer to a child item of the corresponding parent item, we obtain the item using the <tt>TreeItem::child()</tt> function. The <a href="qabstractitemmodel.html#createIndex">createIndex()</a> function is used to create a model index to be returned. We specify the row and column numbers, and a pointer to the item itself. The model index can be used later to obtain the item's data.</p>
 
195
<p>The way that the <tt>TreeItem</tt> objects are defined makes writing the <tt>parent()</tt> function easy:</p>
 
196
<pre>&nbsp;   QModelIndex TreeModel::parent(const QModelIndex &amp;index) const
 
197
    {
 
198
        if (!index.isValid())
 
199
            return QModelIndex();
 
200
 
 
201
        TreeItem *childItem = static_cast&lt;TreeItem*&gt;(index.internalPointer());
 
202
        TreeItem *parentItem = childItem-&gt;parent();
 
203
 
 
204
        if (parentItem == rootItem)
 
205
            return QModelIndex();
 
206
 
 
207
        return createIndex(parentItem-&gt;row(), 0, parentItem);
 
208
    }</pre>
 
209
<p>We only need to ensure that we never return a model index corresponding to the root item. To be consistent with the way that the <tt>index()</tt> function is implemented, we return an invalid model index for the parent of any top-level items in the model.</p>
 
210
<p>When creating a model index to return, we must specify the row and column numbers of the parent item within its own parent. We can easily discover the row number with the <tt>TreeItem::row()</tt> function, and we simply reuse the column number of the child. The model index is created with <a href="qabstractitemmodel.html#createIndex">createIndex()</a> in the same way as in the <tt>index()</tt> function.</p>
 
211
<p>The <tt>rowCount()</tt> function simply returns the number of child items for the <tt>TreeItem</tt> that corresponds to a given model index, or the number of top-level items if an invalid index is specified:</p>
 
212
<pre>&nbsp;   int TreeModel::rowCount(const QModelIndex &amp;parent) const
 
213
    {
 
214
        TreeItem *parentItem;
 
215
 
 
216
        if (!parent.isValid())
 
217
            parentItem = rootItem;
 
218
        else
 
219
            parentItem = static_cast&lt;TreeItem*&gt;(parent.internalPointer());
 
220
 
 
221
        return parentItem-&gt;childCount();
 
222
    }</pre>
 
223
<p>Since each item manages its own column data, the <tt>columnCount()</tt> function has to call the item's own <tt>columnCount()</tt> function to determine how many columns are present for a given model index. As with the <tt>rowCount()</tt> function, if an invalid model index is specified, the number of columns returned is determined from the root item:</p>
 
224
<pre>&nbsp;   int TreeModel::columnCount(const QModelIndex &amp;parent) const
 
225
    {
 
226
        if (parent.isValid())
 
227
            return static_cast&lt;TreeItem*&gt;(parent.internalPointer())-&gt;columnCount();
 
228
        else
 
229
            return rootItem-&gt;columnCount();
 
230
    }</pre>
 
231
<p>Data is obtained from the model via <tt>data()</tt>. Since the item manages its own columns, we need to use the column number to retrieve the data with the <tt>TreeItem::data()</tt> function:</p>
 
232
<pre>&nbsp;   QVariant TreeModel::data(const QModelIndex &amp;index, int role) const
 
233
    {
 
234
        if (!index.isValid())
 
235
            return QVariant();
 
236
 
 
237
        if (role != Qt::DisplayRole)
 
238
            return QVariant();
 
239
 
 
240
        TreeItem *item = static_cast&lt;TreeItem*&gt;(index.internalPointer());
 
241
 
 
242
        return item-&gt;data(index.column());
 
243
    }</pre>
 
244
<p>Note that we only support the <a href="qt.html#ItemDataRole-enum">DisplayRole</a> in this implementation, and we also return invalid <a href="qvariant.html">QVariant</a> objects for invalid model indexes.</p>
 
245
<p>We use the <tt>flags()</tt> function to ensure that views know that the model is read-only:</p>
 
246
<pre>&nbsp;   Qt::ItemFlags TreeModel::flags(const QModelIndex &amp;index) const
 
247
    {
 
248
        if (!index.isValid())
 
249
            return Qt::ItemIsEnabled;
 
250
 
 
251
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 
252
    }</pre>
 
253
<p>The <tt>headerData()</tt> function returns data that we conveniently stored in the root item:</p>
 
254
<pre>&nbsp;   QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
 
255
                                   int role) const
 
256
    {
 
257
        if (orientation == Qt::Horizontal &amp;&amp; role == Qt::DisplayRole)
 
258
            return rootItem-&gt;data(section);
 
259
 
 
260
        return QVariant();
 
261
    }</pre>
 
262
<p>This information could have been supplied in a different way: either specified in the constructor, or hard coded into the <tt>headerData()</tt> function.</p>
 
263
<a name="setting-up-the-data-in-the-model"></a>
 
264
<h2>Setting Up the Data in the Model</h2>
 
265
<p>We use the <tt>setupModelData()</tt> function to set up the initial data in the model. This function parses a text file, extracting strings of text to use in the model, and creates item objects that record both the data and the overall model structure. Naturally, this function works in a way that is very specific to this model. We provide the following description of its behavior, and refer the reader to the example code itself for more information.</p>
 
266
<p>We begin with a text file in the following format:</p>
 
267
<pre>&nbsp;   Getting Started                             How to familiarize yourself with Qt Designer
 
268
        Launching Designer                      Running the Qt Designer application
 
269
        The User Interface                      How to interact with Qt Designer
 
270
        ...</pre>
 
271
<pre>&nbsp;   Connection Editing Mode                     Connecting widgets together with signals and slots
 
272
        Connecting Objects                      Making connections in Qt Designer
 
273
        Editing Connections                     Changing existing connections</pre>
 
274
<p>We process the text file with the following two rules:</p>
 
275
<ul>
 
276
<li>For each pair of strings on each line, create an item (or node) in a tree structure, and place each string in a column of data in the item.</li>
 
277
<li>When the first string on a line is indented with respect to the first string on the previous line, make the item a child of the previous item created.</li>
 
278
</ul>
 
279
<p>To ensure that the model works correctly, it is only necessary to create instances of <tt>TreeItem</tt> with the correct data and parent item.</p>
 
280
<p /><address><hr /><div align="center">
 
281
<table width="100%" cellspacing="0" border="0"><tr class="address">
 
282
<td width="30%">Copyright &copy; 2005 <a href="trolltech.html">Trolltech</a></td>
 
283
<td width="40%" align="center"><a href="trademarks.html">Trademarks</a></td>
 
284
<td width="30%" align="right"><div align="right">Qt 4.0.0</div></td>
 
285
</tr></table></div></address></body>
 
286
</html>