![]() |
Home · All Classes · Main Classes · Annotated · Grouped Classes · Functions | ![]() |
[Previous: Model Classes] [Contents] [Next: View Classes]
The separation of functionality between the components in the model/view architecture enables new models to be written that can take advantage of existing views. This approach lets us present data from a variety of sources using standard graphical user interface components. The interface provided by the QAbstractItemModel class supports models that arrange their data in hierarchical structures, allowing for the possibility that data will be inserted, removed, changed, or sorted in some way. It also provides support for drag and drop operations.
In this chapter, we create a simple read-only model to explore the basic principles of the model/view architecture. Later in this chapter, we will adapt this simple model so that items can be modified by the user.
For an example of a more complex model, see the Simple Tree Model example.
The model implemented here is a simple, non-hierarchical, read-only data model. It has a QStringList as its internal data source, and implements only what is needed to make a functioning model. To make the implementation easier, we subclass QAbstractListModel because it defines sensible default behavior for list models, and it exposes a simpler interface than the QAbstractItemModel class.
When implementing a model it is important to remember that QAbstractItemModel does not store any data itself, it merely presents an interface that the views use to access the data. For a minimal read-only model it is only necessary to implement a few functions as there are default implementations for most of the interface. The class declaration is as follows:
class StringListModel : public QAbstractListModel { Q_OBJECT public: StringListModel(const QStringList &strings, QObject *parent = 0) : QAbstractListModel(parent), stringList(strings) {} int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = DisplayRole) const; ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role = QAbstractListModel::EditRole); bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); private: QStringList stringList; };
Apart from the model's constructor, we only need to implement two functions: rowCount() returns the number of rows in the model, and data() returns an item of data corresponding to a specified model index.
Note that this is a non-hierarchical model, so we don't have to worry about the parent-child relationships. If our model was hierarchical, we would also have to implement the index() and parent() functions.
We want the number of rows in the model to be the same as the number of strings in the string list. We implement the rowCount() function with this in mind:
int StringListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return stringList.count(); }
Since the model is non-hierarchical, we can safely ignore the model index corresponding to the parent item. By default, models derived from QAbstractListModel only contain one column, so we do not need to reimplement the columnCount() function.
For items in the view, we want to return the strings in our string list: The data() function is responsible for returning the data that corresponds to the index argument.
QVariant StringListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == DisplayRole) return stringList.at(index.row()); else return QVariant(); }
If our model is displayed in a view with headers, we want the headers to show the row and column numbers. We can provide information about the headers by subclassing the headerData() function:
QVariant StringListModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) return QString("Column %1").arg(section); else return QString("Row %1").arg(section); }
An item can have several roles, giving out different data depending on the role specified. The items in our model only have one role, DisplayRole, so we return the data for items irrespective of the role specified. However, we could reuse the data we provide for the DisplayRole in other roles, such as the ToolTipRole that views can use to display information about items in a tooltip.
The read-only model shows how simple choices could be presented to the user but, for many applications, an editable list model is much more useful. We can modify the read-only model to make the items editable by implementing two extra functions: flags() and setData(). These are added to the class declaration:
ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value,
A delegate checks whether an item is editable before creating an editor. The model must let the delegate know that its items are editable. We do this by returning the correct flags for each item in the model; in this case, we enable all items and make them both selectable and editable:
QAbstractItemModel::ItemFlags StringListModel::flags(const QModelIndex &index) const { if (!index.isValid()) return ItemIsEnabled; return QAbstractItemModel::flags(index) | ItemIsEditable; }
Note that we do not have to know how the delegate performs the actual editing process. We only have to provide a way for the delegate to set the data in the model. This is achieved through the setData() function:
bool StringListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && role == EditRole) { stringList.replace(index.row(), value.toString()); emit dataChanged(index, index); return true; } return false; }
In this model, the item in the string list that corresponds to the model index specified is replaced by the data provided by the delegate. However, before we actally set the data in the model, we make sure that the index is valid, the item is of the correct type, and that the role is supported. We insist that the role must be the EditRole since this indicates to us that the item is being modified by an editing operation. The underlying data in this model is the same for all roles, so this detail just makes it easier to integrate the model with other standard components.
When the data has been set, the model must let the views know that the data in the model has changed. This is done by emitting the dataChanged() signal. Since only one item has changed, the range of items specified in the signal is limited to just one item.
It is possible to change the number of rows and columns in a model. In the string list model it only makes sense to change the number of rows, so we reimplement the functions for inserting and removing rows. These are declared in the class definition:
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
We implement a function to allow rows to be inserted into the model:
bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent) { Q_UNUSED(parent); for (int row = 0; row < rows; ++row) { stringList.insert(position, ""); } emit rowsInserted(QModelIndex(), position, position+rows-1); return true; }
Since rows in this model correspond to strings in a list, this function inserts a number of empty strings into the list before the given position. The number of strings is equivalent to the number of rows specified. The parent index is normally used to determine which table of items in the model is being referred to. In this case, we only have a single top-level list of string, so we just insert empty strings into that list. We could check the parent index to see whether it corresponds to an item in the list, and return false to indicate failure.
The model emits the rowsInserted() signal to inform other components that the number of rows has changed, specifying the first and last rows that were inserted. Note that the signal is always emitted after the rows are inserted, allowing other components to immediately access the new rows.
The function to remove rows from the model is also simple to write:
bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent) { Q_UNUSED(parent); emit rowsAboutToBeRemoved(QModelIndex(), position, position+rows-1); for (int row = 0; row < rows; ++row) { stringList.removeAt(position); } return true; }
The rows to be removed from the model are specified by the position and the number of rows given. We ignore the parent index to simplify our implementation, and just remove the corresponding items from the string list. The rowsAboutToBeRemoved() signal is always emitted before the rows are removed, specifying the first and last rows to be removed. This is to allow other components to access the data before it is removed.
We can display the contents of this model using the QListView class to present the model's items in the form of a vertical list. For the string list model, this view also provides a default editor so that the items can be manipulated. We examine the possibilities made available by the standard view classes in the introduction to the view classes.
[Previous: Model Classes] [Contents] [Next: View Classes]
Copyright © 2005 Trolltech | Trademarks | Qt 4.0.0-b2 |