Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions | ![]() |
Unlike the Model-View-Controller pattern, the model/view design does not include a completely separate component for managing interaction with the user. Generally, the view is responsible for the presentation of model data to the user, and for processing user input. To allow some flexibility in the way this input is obtained, the interaction is performed by delegates. These components provide input capabilities and are also responsible for rendering individual items in some views. The standard interface for controlling delegates is defined in the QAbstractItemDelegate class.
Delegates are expected to be able to render their contents themselves by implementing the paint() and sizeHint() functions. However, simple widget-based delegates can subclass QItemDelegate instead of QAbstractItemDelegate, and take advantage of the default implementations of those functions. Editors for delegates can be implemented in two ways, either by using widgets to manage the editing process, or by handling events directly. The first approach is covered later in this section.
The standard views provided with Qt use instances of QItemDelegate to provide editing facilities. This default implementation of the delegate interface renders items in the usual style for each of the standard views: QListView, QTableView, and QTreeView. The delegate being used for a view is returned by the itemDelegate() function. The setItemDelegate() function allows you to install a custom delegate for a standard view, and it is necessary to use this function when setting the delegate for a custom view.
The delegate implemented here uses a QSpinBox to provide editing facilities, and is mainly intended for use with models that display integers. Although we set up a custom integer-based table model for this purpose, the way that data in the model is accessed would also allow a string-based model to be used with only minor inconvenience when editing. We also construct a table view so that we can manipulate the model's data.
We subclass the delegate from QItemDelegate because we do not want to write custom display functions. However, we must still provide functions to manage the editor widget:
class SpinBoxDelegate : public QItemDelegate { Q_OBJECT public: SpinBoxDelegate(QObject *parent = 0); QWidget *editor(QWidget *parent, const QStyleOptionViewItem &option, const QAbstractItemModel *model, const QModelIndex &index); void releaseEditor(QWidget *editor); void setEditorData(QWidget *editor, const QAbstractItemModel *model, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QAbstractItemModel* model, const QModelIndex &index) const; protected: bool eventFilter(QObject *object, QEvent *event); private: void acceptEditing(QWidget *editor); void cancelEditing(QWidget *editor); QSpinBox *spinBox; };
Note that no editor widgets are set up when the delegate is constructed. We only construct an editor widget when it is needed.
In this example, when the table view needs to provide an editor, it asks the delegate to provide an editor widget that is appropriate for the item being modified. The editor() function is supplied with everything that the delegate needs to be able to set up a suitable widget.
QWidget *SpinBoxDelegate::editor(QWidget *parent, const QStyleOptionViewItem & /* option */, const QAbstractItemModel * /* model */, const QModelIndex & /* index */) { if (spinBox) cancelEditing(spinBox); spinBox = new QSpinBox(parent); spinBox->setMinimum(0); spinBox->setMaximum(100); spinBox->installEventFilter(this); return spinBox; }
We provide basic editing features with a spin box, checking only that we are allowed to provide an editor, and ignoring the differences between the actions defined by BeginEditAction. The view ensures that we set up the editor's data and geometry correctly by calling functions that we define later for this purpose. Clearly we can create different editors depending on say, the model index, for example if we have a column of integers and a column of strings we could return a QSpinBox or a QLineEdit depending on which column the item to be edited is in.
The delegate must provide a function to copy model data into the editor. In this example, we read the data stored in the display role, and set the value in the spin box accordingly.
void SpinBoxDelegate::setEditorData(QWidget *editor, const QAbstractItemModel *model, const QModelIndex &index) const { int value = model->data(index, QAbstractItemModel::DisplayRole).toInt(); static_cast<QSpinBox *>(editor)->setValue(value); }
In this example, we know that the editor widget is a spin box, but we could have provided different editors for different types of data in the model, in which case we'd need to determine the type used and cast accordingly.
When the user has finished editing the value in the spin box, the view informs the delegate through the releaseEditor() function whether the edited value was accepted or cancelled.
void SpinBoxDelegate::releaseEditor(QWidget *editor) { delete editor; }
If the value was accepted then we use setModelData() (implemented below) to write the value to the model. If the editing operation was cancelled, the value is ignored. The way we handle the editor widget is the same for both cases: we destroy it. A new editor widget will be created by the delegate the next time the view requires one.
Data supplied by the editor is written to the model by the following function. In this example, an integer value is read from the spin box, and written to the model using the index specified:
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { int value = static_cast<QSpinBox *>(editor)->value(); model->setData(index, QAbstractItemModel::EditRole, value); }
All the operations on data are performed through the interface provided by QAbstractItemModel. This makes the delegate mostly independent from the type of data it manipulates, but some assumptions must be made in order to use certain types of editor widgets. In this example, we have assumed that the model always contains integer values, but we can still use this delegate with different kinds of models because QVariant provides sensible default values for unexpected data.
It is the responsibility of the delegate to manage the editor's geometry. The geometry must be set when the editor is created, and when the item's size or position in the view is changed. Fortunately, the view provides all the necessary geometry information inside a view option object.
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QAbstractItemModel* /* model */, const QModelIndex & /* index */) const { editor->setGeometry(option.rect); }
In this case, we just use the geometry information provided by the view option in the item rectangle. A delegate that renders items with several elements would not use the item rectangle directly. It would position the editor in relation to the other elements in the item.
The way the delegate provides editor widgets in this example is quite simple: they are constructed on demand, and deleted when no longer needed. A more sophisticated approach might be used to provide user-friendly editing features, such as a record of previously submitted values.
[Handling Selections] [Up] [Convenience Classes]
Copyright © 2004 Trolltech. | Trademarks | Qt 4.0.0-tp2 |