The Ultimate Qt Community site
Home News Forum Wiki Contest FAQ Links

QAbstractItemModel

From QtCentreWiki

Jump to: navigation, search
WORK IN PROGRESS
This article is under construction and it will be expanded in near future.

Contents

General info

A part of the QtCore module in Qt 4. A base (pure abstract) class for all model implementations that use the MVC paradigm in Qt (InterView).


Subclasses

Using models

Implementing custom models

Model size

The model is a hierarchy of trees of twodimensional tables of items with each tree having an independent size, so one has to provide means to inform about the number of items in each dimension. The size is determined by the number of rows and columns of data for a particular item.


Below mentioned methods should be reimplemented to reflect the model size:

int columnCount ( const QModelIndex & parent = QModelIndex() ) const
This method returns the number of columns for all subitems of a given parent. For most reimplementations this will return a constant number unless different items represent different data structures — for example level 0 represents companies, level 1 represents sections of a particular company and level 2 represents workers in each section.
int rowCount ( const QModelIndex & parent = QModelIndex() ) const
This method returns the number of children of a given parent. For most implementations this will return dynamic values depending on the underlying data structures.
bool hasChildren ( const QModelIndex & parent = QModelIndex() )
This method should be reimplemented to inform the framework whether an item has any children at all (if rowCount returns a value greater than 0)
QModelIndex parent ( const QModelIndex & index ) const
This method should return an index of a parent item of a particular item (most probably constructed using QModelIndex index ( int row, int column, const QModelIndex & parent ) const) or invalid index (QModelIndex()) for top level items.

Internal data representation

The internal data representation depends on the structure of the model itself (its hierarchy) and the backend used to provide data. For self contained data the most frequent data structures used are all kinds of lists, vectors and trees. But there are also models, which only provide an interface to the proper data containers like DOM trees, SQL connections and other. In some cases the data is cached in the model internal structures and synced with its master source from time to time (see dynamic models).

Although the model methods operate on items using the item index concept, sometimes it is not possible or convinient to pinpoint the representation of the item just using the row and column numbers and an index of the parent. Fortunately the index carries more than that. A pointer (void*) or an additional number (int) can be associated with an index, which can point to the internal data representation of the item (either a pointer to a data structure or an index of an element in an array, map or something simmilar). Thanks to that mechanism, methods can have direct access to data structures representing items.

Accessing model data

The main purpose of defining a model is to provide a way to access some data component in a convinient and systematic way. To provide means to retrieve data, the following two methods should be redefined:

ItemFlags flags ( const QModelIndex & index ) const
This method returns a set of properties for a particular item which define its behaviour (state) and range of actions which the item can participate in.

Flags which can be defined for an item:

  • Qt::ItemIsSelectable — item can be selected,
  • Qt::ItemIsEditable — item can be edited,
  • Qt::ItemIsDragEnabled — item can be dragged from the view,
  • Qt::ItemIsDropEnabled — item can be used as a drop target (other items can be dropped on the item),
  • Qt::ItemIsUserCheckable — item can be checked or unchecked by the user,
  • Qt::ItemIsEnabled — the user can interact with the item,
  • Qt::ItemIsTristate — item is checkable with three separate states.


QVariant data ( const QModelIndex & index, int role = DisplayRole ) const
This method is used to retrieve data from the model according to a role name which was given as an argument.
Item roles
Role name Variant Description
General purpose roles
Qt::DisplayRole QString item main (displayed) data
Qt::DecorationRole QPixmap, QIcon item decoration – icon, pixmap or simmilar
Qt::EditRole QString data used in an editor
Qt::ToolTipRole QString data shown as a tooltip
Qt::StatusTipRole QString data shown in the status bar
Qt::WhatsThisRole QString data shown as a "What's this" text
Qt::SizeHintRole QSize item size
Appearence roles
Qt::FontRole QFont item font
Qt::TextAlignmentRole Qt::Alignment text alignment
Qt::BackgroundColorRole QColor, QBrush item background colour
Qt::TextColorRole QColor item foreground colour
Qt::CheckStateRole Qt::CheckState item check state
Accessibility roles
Qt::AccessibleTextRole QString text used by accessibility plugins
Qt::AccessibleDescriptionRole QString description used by accessibility plugins


QVariant headerData ( int section, Orientation orientation, int role = DisplayRole ) const
This method is used to retrieve data from the model corresponding to the indicated section of a header of a given orientation.
It behaves essentially just like the data() method, but returns values associated with column or row headers.

Custom item roles

In many situations roles mentioned earlier are not sufficient to store or retrieve all the data associated with an item. In this situation model designers can create new roles and assign data to them. Custom roles have to be given numbers starting from Qt::UserRole up. Some model methods need to be redefined to teach the model to handle new content.

QMap<int, QVariant> itemData ( const QModelIndex & index ) const
This method returns a packet which contains all data associated with an item. If new roles are created their data should be added to the packet here and returned along with the rest of the roles.

The simplest reimplementation (introducing the MyModel::MyCustomItemRole role) of the itemData() method follows:

    QMap<int, QVariant> MyModel::itemData ( const QModelIndex & index ) const{
        QMap<int, QVariant> m = QAbstractItemModel::itemData(index);
        m[MyCustomItemRole] = data(MyCustomItemRole);
        return m;
    }

Also other methods which take a role id as a parameter need to be altered to handle custom roles just like they handle the built in ones.

Editable models

Models can be editable in such a way, that its data can be modified in one of the views and upon such modifications all views get updated to show new contents of the model.

To make the model editable, one needs to reimplement at least one of the following methods:

bool setData( const QModelIndex & index, const QVariant & value, int role )
Reimplementing this method allows item data to be changed. Default delegate allows modifying data associated with Qt::EditRole (which is in default models equivalent to Qt::DisplayRole), but you can programm your model-view combination to alter some other role as well. Method returns true if the data was modified and false otherwise.
bool setHeaderData( int section, Orientation orientation, const QVariant & value, int role )
Reimplementing this method allows header data to be changed just the way setData() modifies item data.

When reimplementing setData() one has to remember to follow some rules. The most important one is to emit the dataChanged() signal when the data gets altered.

Model contents can also be changed by adding or removing items from it. If one wants to provide a way to add or remove items from the model, the following methods need to be implemented:

bool insertRows( int row, int count, const QModelIndex & parent = QModelIndex() )
This method needs to be reimplemented if you want to add new rows to the model
bool insertColumns ( int column, int count, const QModelIndex & parent = QModelIndex() )
This method needs to be reimplemented if you want to add new columns to the model

Both methods return true on success and false on failure. The first argument for these methods is the index of row/column before which the new rows or columns are to be inserted and the second argument gives the number of columns to be inserted. For example, if you want to insert two columns after the first column, you'll call insertColumns(1, 2).

There are two methods you need to use when reimplementing those methods. Right before you start inserting new columns/rows, you need to call beginInsertColumns() or beginInsertRows() respectively. Right after you are finished inserting, you have to call endInsertColumns() or endInsertRows().

Here is an example implementation of insertRows assuming the model holds its data in a QList<QPair<QString, int> > called m_rows:

    bool CustomModel::insertRows(int row, int count, const QModelIndex &parent){
        if(parent.isValid())
            return false; // the model is flat
        if(rowCount()<row)
            row = rowCount()+1;
        QPair<QString, int> pair(QString::null, 0);
        beginInsertRows(QModelIndex(), row, row+count); // new rows get indexes [row ; row+count]
        for(int i=0;i<count;i++){
            m_rows.insert(row+i, pair);
        }
        endInsertRows();
        return true;
    }

If you don't want to use insert*() and remove*() methods, you can provide your own methods to add and remove data from the model, just remember to emit proper signals to inform the views that they need to fetch the new data and update themselves.

Drag and drop

Drag and drop in Qt implementation of the MVC paradigm is handled by the model and not the view as one might think. The model is responsible for informing the view what kind of items are generated, which items can be the target to drops and which items can be dragged.

Enabling drag and drop for items

Using InterView you can specify which items can be dragged and where can a drop occur. To do this, you should return appropriate tags from the flags() method of the model. Apropriate flags needed to trigger those behaviours are listed near the beginning of this article. If you specify the invalid index as droppable, you'll be able to drop items on the view canvas too.

Specifying acceptable data and starting a drag

Next thing to do when implementing drag and drop for models is to tell the environment, what kind of elements are handled by the model.

QStringList mimeTypes () const
This method specifies, what MIME types the model can generate from its data.

To make items draggable, you also need to provide a way to encode your items into apropriate MIME types. When a drag is started, this method will be invoked, so that selected items can be serialised into the drag object for later handling. Note, that each item can be encoded in many ways (different MIME), so that a wider range of applications can accept those dragged items.

QMimeData * mimeData ( const QModelIndexList & indexes ) const
Reimplement this method to teach your model how to serialise indexes into mime objects


DropActions supportedDropActions () const
This lets the model specify, which drop actions can be performed


Handling the drop

bool dropMimeData ( const QMimeData * data, DropAction action, int row, int column, const QModelIndex & parent )

Dynamic models

Disadvantages of Qt model specification

Web resources

Personal tools