blob: 68e10e3e7815f67bcff246f2d8709e9baaf2e838 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example itemviews/editabletreemodel
\title Editable Tree Model Example
\ingroup examples-itemviews
\brief This example shows how to implement a simple item-based tree model that can
be used with other classes the model/view framework.
\image itemviews-editabletreemodel.png
The model supports editable items, custom headers, and the ability to
insert and remove rows and columns. With these features, it is also
possible to insert new child items, and this is shown in the supporting
example code.
\section1 Overview
As described in the \l{Model Subclassing Reference}, models must
provide implementations for the standard set of model functions:
\l{QAbstractItemModel::}{flags()}, \l{QAbstractItemModel::}{data()},
\l{QAbstractItemModel::}{headerData()},
\l{QAbstractItemModel::}{columnCount()}, and
\l{QAbstractItemModel::}{rowCount()}. In addition, hierarchical models,
such as this one, need to provide implementations of
\l{QAbstractItemModel::}{index()} and \l{QAbstractItemModel::}{parent()}.
An editable model needs to provide implementations of
\l{QAbstractItemModel::}{setData()} and
\l{QAbstractItemModel::}{setHeaderData()}, and must return a suitable
combination of flags from its \l{QAbstractItemModel::}{flags()} function.
Since this example allows the dimensions of the model to be changed,
we must also implement \l{QAbstractItemModel::}{insertRows()},
\l{QAbstractItemModel::}{insertColumns()},
\l{QAbstractItemModel::}{removeRows()}, and
\l{QAbstractItemModel::}{removeColumns()}.
\section1 Design
As with the \l{itemviews/simpletreemodel}{Simple Tree Model} example,
the model simply acts as a wrapper around a collection
of instances of a \c TreeItem class. Each \c TreeItem is designed to
hold data for a row of items in a tree view, so it contains a list of
values corresponding to the data shown in each column.
Since QTreeView provides a row-oriented view onto a model, it is
natural to choose a row-oriented design for data structures that
will supply data via a model to this kind of view. Although this makes
the tree model less flexible, and possibly less useful for use with
more sophisticated views, it makes it less complex to design and easier
to implement.
\target Relations-between-internal-items
\table
\row \li \inlineimage itemviews-editabletreemodel-items.png
\li \b{Relations between internal items}
When designing a data structure for use with a custom model, it is useful
to expose each item's parent via a function like
\l{TreeItem::parent}{TreeItem::parent()} because it will make
writing the model's own \l{QAbstractItemModel::}{parent()} function easier.
Similarly, a function like \l{TreeItem::child}{TreeItem::child()} is
helpful when implementing the model's \l{QAbstractItemModel::}{index()}
function. As a result, each \c TreeItem maintains information about
its parent and children, making it possible for us to traverse the tree
structure.
The diagram shows how \c TreeItem instances are connected via their
\l{TreeItem::parent}{parent()} and \l{TreeItem::child}{child()}
functions.
In the example shown, two top-level items, \b{A} and
\b{B}, can be obtained from the root item by calling its child()
function, and each of these items return the root node from their
parent() functions, though this is only shown for item \b{A}.
\endtable
Each \c TreeItem stores data for each column in the row it represents
in its \c itemData private member (a list of QVariant objects).
Since there is a one-to-one mapping between each column in the view
and each entry in the list, we provide a simple
\l{TreeItem::data}{data()} function to read entries in the \c itemData
list and a \l{TreeItem::setData}{setData()} function to allow them to
be modified.
As with other functions in the item, this simplifies the implemention
of the model's \l{QAbstractItemModel::}{data()} and
\l{QAbstractItemModel::}{setData()} functions.
We place an item at the root of the tree of items. This root item
corresponds to the null model index, \l{QModelIndex::}{QModelIndex()},
that is used to represent the parent of a top-level item when handling
model indexes.
Although the root item does not have a visible representation in any of
the standard views, we use its internal list of QVariant objects to
store a list of strings that will be passed to views for use as
horizontal header titles.
\table
\row \li \inlineimage itemviews-editabletreemodel-model.png
\li \b{Accessing data via the model}
In the case shown in the diagram, the piece of information represented
by \b{a} can be obtained using the standard model/view API:
\code
QVariant a = model->index(0, 0, QModelIndex()).data();
\endcode
Since each items holds pieces of data for each column in a given row,
there can be many model indexes that map to the same \c TreeItem object.
For example, the information represented by \b{b} can be obtained
using the following code:
\code
QVariant b = model->index(1, 0, QModelIndex()).data();
\endcode
The same underlying \c TreeItem would be accessed to obtain information
for the other model indexes in the same row as \b{b}.
\endtable
In the model class, \c TreeModel, we relate \c TreeItem objects to
model indexes by passing a pointer for each item when we create its
corresponding model index with QAbstractItemModel::createIndex() in
our \l{TreeModel::index}{index()} and \l{TreeModel::parent}{parent()}
implementations.
We can retrieve pointers stored in this way by calling the
\l{QModelIndex::}{internalPointer()} function on the relevant model
index - we create our own \l{TreeModel::getItem}{getItem()} function to
do the work for us, and call it from our \l{TreeModel::data}{data()}
and \l{TreeModel::parent}{parent()} implementations.
Storing pointers to items is convenient when we control how they are
created and destroyed since we can assume that an address obtained from
\l{QModelIndex::}{internalPointer()} is a valid pointer.
However, some models need to handle items that are obtained from other
components in a system, and in many cases it is not possible to fully
control how items are created or destroyed. In such situations, a pure
pointer-based approach needs to be supplemented by safeguards to ensure
that the model does not attempt to access items that have been deleted.
\table
\row \li \b{Storing information in the underlying data structure}
Several pieces of data are stored as QVariant objects in the \c itemData
member of each \c TreeItem instance.
The diagram shows how pieces of information,
represented by the labels \b{a}, \b{b} and \b{c} in the
previous two diagrams, are stored in items \b{A}, \b{B} and
\b{C} in the underlying data structure. Note that pieces of
information from the same row in the model are all obtained from the
same item. Each element in a list corresponds to a piece of information
exposed by each column in a given row in the model.
\li \inlineimage itemviews-editabletreemodel-values.png
\endtable
Since the \c TreeModel implementation has been designed for use with
QTreeView, we have added a restriction on the way it uses \c TreeItem
instances: each item must expose the same number of columns of data.
This makes viewing the model consistent, allowing us to use the root
item to determine the number of columns for any given row, and only
adds the requirement that we create items containing enough data for
the total number of columns. As a result, inserting and removing
columns are time-consuming operations because we need to traverse the
entire tree to modify every item.
An alternative approach would be to design the \c TreeModel class so
that it truncates or expands the list of data in individual \c TreeItem
instances as items of data are modified. However, this "lazy" resizing
approach would only allow us to insert and remove columns at the end of
each row and would not allow columns to be inserted or removed at
arbitrary positions in each row.
\target Relating-items-using-model-indexes
\table
\row
\li \inlineimage itemviews-editabletreemodel-indexes.png
\li \b{Relating items using model indexes}
As with the \l{itemviews/simpletreemodel}{Simple Tree Model} example,
the \c TreeModel needs to be able to take a model index, find the
corresponding \c TreeItem, and return model indexes that correspond to
its parents and children.
In the diagram, we show how the model's \l{TreeModel::parent}{parent()}
implementation obtains the model index corresponding to the parent of
an item supplied by the caller, using the items shown in a
\l{Relations-between-internal-items}{previous diagram}.
A pointer to item \b{C} is obtained from the corresponding model index
using the \l{QModelIndex::internalPointer()} function. The pointer was
stored internally in the index when it was created. Since the child
contains a pointer to its parent, we use its \l{TreeItem::parent}{parent()}
function to obtain a pointer to item \b{B}. The parent model index is
created using the QAbstractItemModel::createIndex() function, passing
the pointer to item \b{B} as the internal pointer.
\endtable
\section1 TreeItem Class Definition
The \c TreeItem class provides simple items that contain several
pieces of data, including information about their parent and
child items:
\snippet itemviews/editabletreemodel/treeitem.h 0
We have designed the API to be similar to that provided by
QAbstractItemModel by giving each item functions to return the number
of columns of information, read and write data, and insert and remove
columns. However, we make the relationship between items explicit by
providing functions to deal with "children" rather than "rows".
Each item contains a list of pointers to child items, a pointer to its
parent item, and a list of QVariant objects that correspond to
information held in columns in a given row in the model.
\section1 TreeItem Class Implementation
Each \c TreeItem is constructed with a list of data and an optional
parent item:
\snippet itemviews/editabletreemodel/treeitem.cpp 0
Initially, each item has no children. These are added to the item's
internal \c childItems member using the \c insertChildren() function
described later.
The destructor ensures that each child added to the item is deleted
when the item itself is deleted:
\snippet itemviews/editabletreemodel/treeitem.cpp 1
\target TreeItem::parent
Since each item stores a pointer to its parent, the \c parent() function
is trivial:
\snippet itemviews/editabletreemodel/treeitem.cpp 9
\target TreeItem::child
Three functions provide information about the children of an item.
\c child() returns a specific child from the internal list of children:
\snippet itemviews/editabletreemodel/treeitem.cpp 2
The \c childCount() function returns the total number of children:
\snippet itemviews/editabletreemodel/treeitem.cpp 3
The \c childNumber() function is used to determine the index of the child
in its parent's list of children. It accesses the parent's \c childItems
member directly to obtain this information:
\snippet itemviews/editabletreemodel/treeitem.cpp 4
The root item has no parent item; for this item, we return zero to be
consistent with the other items.
The \c columnCount() function simply returns the number of elements in
the internal \c itemData list of QVariant objects:
\snippet itemviews/editabletreemodel/treeitem.cpp 5
\target TreeItem::data
Data is retrieved using the \c data() function, which accesses the
appropriate element in the \c itemData list:
\snippet itemviews/editabletreemodel/treeitem.cpp 6
\target TreeItem::setData
Data is set using the \c setData() function, which only stores values
in the \c itemData list for valid list indexes, corresponding to column
values in the model:
\snippet itemviews/editabletreemodel/treeitem.cpp 11
To make implementation of the model easier, we return true to indicate
that the data was set successfully.
Editable models often need to be resizable, enabling rows and columns to
be inserted and removed. The insertion of rows beneath a given model index
in the model leads to the insertion of new child items in the corresponding
item, handled by the \c insertChildren() function:
\snippet itemviews/editabletreemodel/treeitem.cpp 7
This ensures that new items are created with the required number of columns
and inserted at a valid position in the internal \c childItems list.
Items are removed with the \c removeChildren() function:
\snippet itemviews/editabletreemodel/treeitem.cpp 10
As discussed above, the functions for inserting and removing columns are
used differently to those for inserting and removing child items because
they are expected to be called on every item in the tree. We do this by
recursively calling this function on each child of the item:
\snippet itemviews/editabletreemodel/treeitem.cpp 8
\section1 TreeModel Class Definition
The \c TreeModel class provides an implementation of the QAbstractItemModel
class, exposing the necessary interface for a model that can be edited and
resized.
\snippet itemviews/editabletreemodel/treemodel.h 0
The constructor and destructor are specific to this model.
\snippet itemviews/editabletreemodel/treemodel.h 1
Read-only tree models only need to provide the above functions. The
following public functions provide support for editing and resizing:
\snippet itemviews/editabletreemodel/treemodel.h 2
To simplify this example, the data exposed by the model is organized into
a data structure by the model's \l{TreeModel::setupModelData}{setupModelData()}
function. Many real world models will not process the raw data at all, but
simply work with an existing data structure or library API.
\section1 TreeModel Class Implementation
The constructor creates a root item and initializes it with the header
data supplied:
\snippet itemviews/editabletreemodel/treemodel.cpp 0
We call the internal \l{TreeModel::setupModelData}{setupModelData()}
function to convert the textual data supplied to a data structure we can
use with the model. Other models may be initialized with a ready-made
data structure, or use an API from a library that maintains its own data.
The destructor only has to delete the root item, which will cause all child
items to be recursively deleted.
\snippet itemviews/editabletreemodel/treemodel.cpp 1
\target TreeModel::getItem
Since the model's interface to the other model/view components is based
on model indexes, and since the internal data structure is item-based,
many of the functions implemented by the model need to be able to convert
any given model index to its corresponding item. For convenience and
consistency, we have defined a \c getItem() function to perform this
repetitive task:
\snippet itemviews/editabletreemodel/treemodel.cpp 4
Each model index passed to this function should correspond to a valid
item in memory. If the index is invalid, or its internal pointer does
not refer to a valid item, the root item is returned instead.
The model's \c rowCount() implementation is simple: it first uses the
\c getItem() function to obtain the relevant item; then it returns the
number of children it contains:
\snippet itemviews/editabletreemodel/treemodel.cpp 8
By contrast, the \c columnCount() implementation does not need to look
for a particular item because all items are defined to have the same
number of columns associated with them.
\snippet itemviews/editabletreemodel/treemodel.cpp 2
As a result, the number of columns can be obtained directly from the root
item.
To enable items to be edited and selected, the \c flags() function needs
to be implemented so that it returns a combination of flags that includes
the Qt::ItemIsEditable and Qt::ItemIsSelectable flags as well as
Qt::ItemIsEnabled:
\snippet itemviews/editabletreemodel/treemodel.cpp 3
\target TreeModel::index
The model needs to be able to generate model indexes to allow other
components to request data and information about its structure. This task
is performed by the \c index() function, which is used to obtain model
indexes corresponding to children of a given parent item:
\snippet itemviews/editabletreemodel/treemodel.cpp 5
In this model, we only return model indexes for child items if the parent
index is invalid (corresponding to the root item) or if it has a zero
column number.
We use the custom \l{TreeModel::getItem}{getItem()} function to obtain
a \c TreeItem instance that corresponds to the model index supplied, and
request its child item that corresponds to the specified row.
\snippet itemviews/editabletreemodel/treemodel.cpp 6
Since each item contains information for an entire row of data, we create
a model index to uniquely identify it by calling
\l{QAbstractItemModel::}{createIndex()} it with the row and column numbers
and a pointer to the item. In the \l{TreeModel::data}{data()} function,
we will use the item pointer and column number to access the data
associated with the model index; in this model, the row number is not
needed to identify data.
\target TreeModel::parent
The \c parent() function supplies model indexes for parents of items
by finding the corresponding item for a given model index, using its
\l{TreeItem::parent}{parent()} function to obtain its parent item,
then creating a model index to represent the parent. (See
\l{Relating-items-using-model-indexes}{the above diagram}).
\snippet itemviews/editabletreemodel/treemodel.cpp 7
Items without parents, including the root item, are handled by returning
a null model index. Otherwise, a model index is created and returned as
in the \l{TreeModel::index}{index()} function, with a suitable row number,
but with a zero column number to be consistent with the scheme used in
the \l{TreeModel::index}{index()} implementation.
\target TreeModel::data
\target TreeModel::setupModelData
*/