QAbstractItemModel
From QtCentreWiki
| WORK IN PROGRESS This article is under construction and it will be expanded in near future. |
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
- QAbstractListModel — abstract model for handling lists
- QAbstractProxyModel — abstract model for proxying models
- QAbstractTableModel — abstract model for handling tabular data
- QDirModel — filesystem model
- QProxyModel (deprecated)
- QStandardItemModel — general use item model
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
rowCountreturns 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
- Trolltech's docs (Qt 4)
- Task Tracker


