Qt 4.6: Model Subclassing Reference

|  | Home · All Classes · Overviews |

[Previous: Proxy Models] [Contents]

# Model Subclassing Reference

- Introduction
- Item Data Handling

- Read-Only Access
- Editable Items
- Resizable Models
- Lazy Population of Model Data

- Navigation and Model Index Creation

- Parents and Children

- Drag and Drop Support and MIME Type Handling

- MIME Data
- Accepting Dropped Data
- Convenience Views

- Performance Optimization for Large Amounts of Data

## Introduction

Model subclasses need to provide implementations of many of the virtual functions defined in the QAbstractItemModel base class. The number of these functions that need to be implemented depends on the type of model - whether it supplies views with a simple list, a table, or a complex hierarchy of items. Models that inherit from QAbstractListModel and QAbstractTableModel can take advantage of the default implementations of functions provided by those classes. Models that expose items of data in tree-like structures must provide implementations for many of the virtual functions in QAbstractItemModel.

The functions that need to be implemented in a model subclass can be divided into three groups:

- Item data handling: All models need to implement functions to enable views and delegates to query the dimensions of the model, examine items, and retrieve data.
- Navigation and index creation: Hierarchical models need to provide functions that views can call to navigate the tree-like structures they expose, and obtain model indexes for items.
- Drag and drop support and MIME type handling: Models inherit functions that control the way that internal and external drag and drop operations are performed. These functions allow items of data to be described in terms of MIME types that other components and applications can understand.

For more information, see the "Item View Classes" Chapter of C++ GUI Programming with Qt 4.

## Item Data Handling

Models can provide varying levels of access to the data they provide: They can be simple read-only components, some models may support resizing operations, and others may allow items to be edited.

### Read-Only Access

To provide read-only access to data provided by a model, the following functions must be implemented in the model's subclass:

flags() | Used by other components to obtain information about each item provided by the model. In many models, the combination of flags should include Qt::ItemIsEnabled and Qt::ItemIsSelectable.
data() | Used to supply item data to views and delegates. Generally, models only need to supply data for Qt::DisplayRole and any application-specific user roles, but it is also good practice to provide data for Qt::ToolTipRole, Qt::AccessibleTextRole, and Qt::AccessibleDescriptionRole. See the Qt::ItemDataRole enum documentation for information about the types associated with each role.
headerData() | Provides views with information to show in their headers. The information is only retrieved by views that can display header information.
rowCount() | Provides the number of rows of data exposed by the model.

These four functions must be implemented in all types of model, including list models (QAbstractListModel subclasses) and table models (QAbstractTableModel subclasses).

Additionally, the following functions must be implemented in direct subclasses of QAbstractTableModel and QAbstractItemModel:

columnCount() | Provides the number of columns of data exposed by the model. List models do not provide this function because it is already implemented in QAbstractListModel.

### Editable Items

Editable models allow items of data to be modified, and may also provide functions to allow rows and columns to be inserted and removed. To enable editing, the following functions must be implemented correctly:

flags() | Must return an appropriate combination of flags for each item. In particular, the value returned by this function must include Qt::ItemIsEditable in addition to the values applied to items in a read-only model.
setData() | Used to modify the item of data associated with a specified model index. To be able to accept user input, provided by user interface elements, this function must handle data associated with Qt::EditRole. The implementation may also accept data associated with many different kinds of roles specified by Qt::ItemDataRole. After changing the item of data, models must emit the dataChanged() signal to inform other components of the change.
setHeaderData() | Used to modify horizontal and vertical header information. After changing the item of data, models must emit the headerDataChanged() signal to inform other components of the change.

### Resizable Models

All types of model can support the insertion and removal of rows. Table models and hierarchical models can also support the insertion and removal of columns. It is important to notify other components about changes to the model's dimensions both before and after they occur. As a result, the following functions can be implemented to allow the model to be resized, but implementations must ensure that the appropriate functions are called to notify attached views and delegates:

insertRows() | Used to add new rows and items of data to all types of model. Implementations must call beginInsertRows() before inserting new rows into any underlying data structures, and call endInsertRows() immediately afterwards.
removeRows() | Used to remove rows and the items of data they contain from all types of model. Implementations must call beginRemoveRows() before inserting new columns into any underlying data structures, and call endRemoveRows() immediately afterwards.
insertColumns() | Used to add new columns and items of data to table models and hierarchical models. Implementations must call beginInsertColumns() before rows are removed from any underlying data structures, and call endInsertColumns() immediately afterwards.
removeColumns() | Used to remove columns and the items of data they contain from table models and hierarchical models. Implementations must call beginRemoveColumns() before columns are removed from any underlying data structures, and call endRemoveColumns() immediately afterwards.

Generally, these functions should return true if the operation was successful. However, there may be cases where the operation only partly succeeded; for example, if less than the specified number of rows could be inserted. In such cases, the model should return false to indicate failure to enable any attached components to handle the situation.

The signals emitted by the functions called in implementations of the resizing API give attached components the chance to take action before any data becomes unavailable. The encapsulation of insert and remove operations with begin and end functions also enable the model to manage persistent model indexes correctly.

Normally, the begin and end functions are capable of informing other components about changes to the model's underlying structure. For more complex changes to the model's structure, perhaps involving internal reorganization or sorting of data, it is necessary to emit the layoutChanged() signal to cause any attached views to be updated.

### Lazy Population of Model Data

Lazy population of model data effectively allows requests for information about the model to be deferred until it is actually needed by views.

Some models need to obtain data from remote sources, or must perform time-consuming operations to obtain information about the way the data is organized. Since views generally request as much information as possible in order to accurately display model data, it can be useful to restrict the amount of information returned to them to reduce unnecessary follow-up requests for data.

In hierarchical models where finding the number of children of a given item is an expensive operation, it is useful to ensure that the model's rowCount() implementation is only called when necessary. In such cases, the hasChildren() function can be reimplemented to provide an inexpensive way for views to check for the presence of children and, in the case of QTreeView, draw the appropriate decoration for their parent item.

Whether the reimplementation of hasChildren() returns `true` or `false`, it may not be necessary for the view to call rowCount() to find out how many children are present. For example, QTreeView does not need to know how many children there are if the parent item has not been expanded to show them.

If it is known that many items will have children, reimplementing hasChildren() to unconditionally return `true` is sometimes a useful approach to take. This ensures that each item can be later examined for children while making initial population of model data as fast as possible. The only disadvantage is that items without children may be displayed incorrectly in some views until the user attempts to view the non-existent child items.

## Navigation and Model Index Creation

Hierarchical models need to provide functions that views can call to navigate the tree-like structures they expose, and obtain model indexes for items.

### Parents and Children

Since the structure exposed to views is determined by the underlying data structure, it is up to each model subclass to create its own model indexes by providing implementations of the following functions:

index() | Given a model index for a parent item, this function allows views and delegates to access children of that item. If no valid child item - corresponding to the specified row, column, and parent model index, can be found, the function must return QModelIndex(), which is an invalid model index.
parent() | Provides a model index corresponding to the parent of any given child item. If the model index specified corresponds to a top-level item in the model, or if there is no valid parent item in the model, the function must return an invalid model index, created with the empty QModelIndex() constructor.

Both functions above use the createIndex() factory function to generate indexes for other components to use. It is normal for models to supply some unique identifier to this function to ensure that the model index can be re-associated with its corresponding item later on.

## Drag and Drop Support and MIME Type Handling

The model/view classes support drag and drop operations, providing default behavior that is sufficient for many applications. However, it is also possible to customize the way items are encoded during drag and drop operations, whether they are copied or moved by default, and how they are inserted into existing models.

Additionally, the convenience view classes implement specialized behavior that should closely follow that expected by existing developers. The Convenience Views section provides an overview of this behavior.

### MIME Data

By default, the built-in models and views use an internal MIME type (`application/x-qabstractitemmodeldatalist`) to pass around information about model indexes. This specifies data for a list of items, containing the row and column numbers of each item, and information about the roles that each item supports.

Data encoded using this MIME type can be obtained by calling QAbstractItemModel::mimeData() with a QModelIndexList containing the items to be serialized.

When implementing drag and drop support in a custom model, it is possible to export items of data in specialized formats by reimplementing the following function:

mimeData() | This function can be reimplemented to return data in formats other than the default `application/x-qabstractitemmodeldatalist` internal MIME type. Subclasses can obtain the default QMimeData object from the base class and add data to it in additional formats.

For many models, it is useful to provide the contents of items in common format represented by MIME types such as `text/plain` and `image/png`. Note that images, colors and HTML documents can easily be added to a QMimeData object with the QMimeData::setImageData(), QMimeData::setColorData(), and QMimeData::setHtml() functions.

### Accepting Dropped Data

When a drag and drop operation is performed over a view, the underlying model is queried to determine which types of operation it supports and the MIME types it can accept. This information is provided by the QAbstractItemModel::supportedDropActions() and QAbstractItemModel::mimeTypes() functions. Models that do not override the implementations provided by QAbstractItemModel support copy operations and the default internal MIME type for items.

When serialized item data is dropped onto a view, the data is inserted into the current model using its implementation of QAbstractItemModel::dropMimeData(). The default implementation of this function will never overwrite any data in the model; instead, it tries to insert the items of data either as siblings of an item, or as children of that item.

To take advantage of QAbstractItemModel's default implementation for the built-in MIME type, new models must provide reimplementations of the following functions:

insertRows() | These functions enable the model to automatically insert new data using the existing implementation provided by QAbstractItemModel::dropMimeData().
insertColumns()
setData() | Allows the new rows and columns to be populated with items.
setItemData() | This function provides more efficient support for populating new items.

To accept other forms of data, these functions must be reimplemented:

supportedDropActions() | Used to return a combination of drop actions, indicating the types of drag and drop operations that the model accepts.
mimeTypes() | Used to return a list of MIME types that can be decoded and handled by the model. Generally, the MIME types that are supported for input into the model are the same as those that it can use when encoding data for use by external components.
dropMimeData() | Performs the actual decoding of the data transferred by drag and drop operations, determines where in the model it will be set, and inserts new rows and columns where necessary. How this function is implemented in subclasses depends on the requirements of the data exposed by each model.

If the implementation of the dropMimeData() function changes the dimensions of a model by inserting or removing rows or columns, or if items of data are modified, care must be taken to ensure that all relevant signals are emitted. It can be useful to simply call reimplementations of other functions in the subclass, such as setData(), insertRows(), and insertColumns(), to ensure that the model behaves consistently.

In order to ensure drag operations work properly, it is important to reimplement the following functions that remove data from the model:

- removeRows()
- removeRow()
- removeColumns()
- removeColumn()

For more information about drag and drop with item views, refer to Using Drag and Drop with Item Views.

### Convenience Views

The convenience views (QListWidget, QTableWidget, and QTreeWidget) override the default drag and drop functionality to provide less flexible, but more natural behavior that is appropriate for many applications. For example, since it is more common to drop data into cells in a QTableWidget, replacing the existing contents with the data being transferred, the underlying model will set the data of the target items rather than insert new rows and columns into the model. For more information on drag and drop in convenience views, you can see Using Drag and Drop with Item Views.

## Performance Optimization for Large Amounts of Data

The canFetchMore() function checks if the parent has more data available and returns true or false accordingly. The fetchMore() function fetches data based on the parent specified. Both these functions can be combined, for example, in a database query involving incremental data to populate a QAbstractItemModel. We reimplement canFetchMore() to indicate if there is more data to be fetched and fetchMore() to populate the model as required.

Another example would be dynamically populated tree models, where we reimplement fetchMore() when a branch in the tree model is expanded.

If your reimplementation of fetchMore() adds rows to the model, you need to call beginInsertRows() and endInsertRows(). Also, both canFetchMore() and fetchMore() must be reimplemented as their default implementation returns false and does nothing.

[Previous: Proxy Models] [Contents]