Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions

[Previous: Model/View Programming] [Contents] [Next: Creating New Models]

Model Classes

Basic Concepts

In the model/view architecture, the model's purpose is to manage data, and to provide a standard representation of that data to the other components in the framework. In Qt, the standard interface to item models is defined by the QAbstractItemModel class. This class exposes a table-like interface to other components, regardless of how the data is actually stored in the model or presented to the user. It also notifies any attached views of changes to the model through the signals and slots mechanism.

This section describes some basic concepts that are central to the way models are accessed by other components. More advanced concepts are discussed later.

Model Indexes

To ensure that representation of the data in the model is kept separate from the way it is accessed, the concept of a model index is introduced. Each item of data is represented by a model index that is provided by the model when required, and that knows which model it belongs to.

    QAbstractItemModel *model = index.model();

As a result, only the model needs to know how items are stored, and the type of data managed by the model can be defined fairly generally.

Model indexes provide temporary references to items, and can be used to retrieve or modify their data. Since models may reorganize their internal structures from time to time, model indexes may become invalid, and should not be stored. If a long-term reference to a model item is required, a persistent model index must be created. This provides a reference to the item that the model keeps up-to-date. Temporary model indexes are provided by the QModelIndex class, and persistent model indexes are provided by the QPersistentModelIndex class.

To obtain a model index for an item in a model, three properties must be specified: a row number, a column number, and the model index of a parent item. The following sections describe and explain these properties in detail.

Rows and Columns

In its most basic form, an item model can be accessed as a simple table in which items are located by their row and column numbers. This does not mean that the data in the model has to be stored in a tabular structure - the use of row and column numbers is only a convention to allow components to communicate with each other. We can retrieve information about any given item by specifying its row and column numbers to the model, and we receive an index that represents the item:

    QModelIndex index = model->index(row, column, ...);

We can ask the model for the item's data by passing it the model index corresponding to the item:

    QVariant value = model->data(index, ...);

The following diagram shows a representation of a basic model in which each item is located by a pair of row and column numbers.

By specifying the relevant row and column numbers to the model we obtain a model index that represents an item (shown highlighted) in the model.

Parents of Items

A simple tabular model is ideal for representing items in a table, or for representing lists of items in just a single column of a tabular structure. However, structures such as tree views require the model to expose a more flexible interface to the items within. As a result, each item can also be the parent of another table of items, in much the same way that a top-level item in a tree view can contain another list of items.

When requesting an index for a model item, we must provide some information about the item's parent. Outside the model, the only way to refer to an item is through a model index, so a parent model index must also be given:

    QModelIndex index = model->index(row, column, parent);

The following diagram shows the row numbers, column numbers, and parent indexes for two items in the model: a top-level item, and one of its children.

The model index of the highlighted top-level item is represented by the 'X' symbol, and this is given as the parent of the highlighted child item. Top-level items in a model do not have a parent model index to refer to, so we ensure that the model knows that we are referring to a top-level item by specifying a Null model index. This is a convention used by the model/view classes. We can obtain the model indexes for both items shown in the diagram:

    QModelIndex topLevelIndex = model->index(2, 1, QModelIndex()); // 'X'
    QModelIndex childIndex = model->index(3, 4, topLevelIndex);

Note that we used QModelIndex() to construct an invalid model index.

Item Roles

Items in a model can perform various roles for other components, allowing different kinds of data to be supplied for different situations. For example, the DisplayRole role is used to access a string that can be displayed as text in a view. Typically, items contain data for a number of different roles.

The role indicates to the model which type of data is being referred to. Views can display the roles in different ways, so it is important to supply appropriate information for each role. We will examine roles in more detail when we create an example model.

Summary of Concepts

Model indexes contain information about the location of items within a model. Items are referred to by their row and column numbers, and by the model index of their parent item. Model indexes are constructed by models at the request of other components, such as views and delegates.

If a valid parent index is specified when an index is constructed, the index refers to a child of the corresponding parent item; otherwise the index refers to a top-level item in the model.

The role distinguishes between the different kinds of data associated with an item.

Using an Existing Model

One of the models provided by Qt is the QDirModel, a model that maintains information about the contents of a directory. This provides a ready-to-use model to experiment with, and can be easily set up using existing data. Using this model, we can show how to set up a model for use with ready-made views, and explore how to manipulate data using model indexes.

Using Views with a Model

The QListView and QTreeView classes are the most suitable views to use with QDirModel. The example presented below displays the contents of a directory in a tree view next to the same information in a list view. The views share the user's selection so that the selected items are highlighted in both views.

We set up a QDirModel so that it is ready for use, and create some views to display the contents of a directory. This shows the simplest pattern of use of a model. The construction and use of the model is performed from within a single main() function:

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        QSplitter *splitter = new QSplitter;

        QDirModel *model = new QDirModel(QDir(), splitter);

The model is set up to use data from a default directory, and is given an arbitrary widget as its parent object. The parent can be any subclass of QObject. We create two views so that we can examine the items held in the model:

        QTreeView *tree = new QTreeView(splitter);
        QListView *list = new QListView(splitter);

        tree->setModel(model);
        list->setModel(model);

The views are constructed in the same way as other widgets. Setting up a view to display the items in the model is simply a matter of calling its setModel() function with the directory model as the argument.

The rest of the function just displays the views within a splitter widget, and runs the application's event loop:

        splitter->setWindowTitle("Two views onto the same directory model");
        splitter->show();
        app.setMainWidget(splitter);

        return app.exec();
    }

In the above example, we neglected to mention how to handle selections of items. This subject is covered in more detail in the handling selections section.

Using Model Indexes

To demonstrate how data can be retrieved from a model, using model indexes, we set up a QDirModel without a view and print out the names of files and directories to a terminal. We will use the qDebug() function for this purpose. Although this does not show a normal way of using a model, it demonstrates the conventions used by models when dealing with model indexes.

We construct a directory model in the same way as before:

        QDirModel *model = new QDirModel(QDir(), window);
        int numRows = model->rowCount(QModelIndex());

In this case, we ensure that all files and directories are reported by the model by setting the file filter, and we count the number of rows in the model using the rowCount() function.

For simplicity, we are only interested in the items in the first column of the model. We examine each row in turn, obtaining a model index for the first item in each row, and reading the data stored for that item in the model.

        for (int row = 0; row < numRows; ++row) {
            QModelIndex index = model->index(row, 0, QModelIndex());

To obtain a model index, we specify the row number, column number (zero for the first column), and the appropriate parent model index for top-level model items (the default invalid model index). The text stored in each item is retrieved using the model's data() function. We specify the model index and the DisplayRole to obtain data for the item in the form of a string.

            QString text = model->data(index, QAbstractItemModel::DisplayRole).toString();
            qDebug("%s", text.ascii());

        }

The above example demonstrates the basic principles used to retrieve data from a model:

Further Reading

New models can be created by implementing the standard interface provided by QAbstractItemModel. We will demonstrate this by creating a new model to provide a convenient ready-to-use model for holding strings.

[Previous: Model/View Programming] [Contents] [Next: Creating New Models]


Copyright © 2004 Trolltech Trademarks
Qt 4.0.0-b1