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 ues 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 contain numerical data in the form of 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) : QItemDelegate(parent) {} QItemDelegate::EditorType editorType(const QAbstractItemModel *model, const QModelIndex &index) const; QWidget *editor(BeginEditAction action, QWidget *parent, const QStyleOptionViewItem &option, const QAbstractItemModel *model, const QModelIndex &index); void releaseEditor(EndEditAction action, QWidget *editor, QAbstractItemModel *model, const QModelIndex &index); 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; };
Note that no editors widgets are set up when the delegate is constructed. We will construct an editor widget when it is needed.
The editorType() function returns a value describing the type of editor that the delegate provides. This delegate only supports widget-based editing, so the implementation of this function is very simple:
QItemDelegate::EditorType SpinBoxDelegate::editorType(const QAbstractItemModel * /* model */, const QModelIndex & /* index */) const { return QItemDelegate::Widget; }
The default implementation of this function informs the view that it provides an event-based editor. Therefore, you must reimplement this function if you want to provide a widget-based editor.
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(BeginEditAction action, QWidget *parent, const QStyleOptionViewItem & /* option */, const QAbstractItemModel * /* model */, const QModelIndex & /* index */) { if (action != QItemDelegate::NeverEdit) { QSpinBox *spinBox = new QSpinBox(parent); spinBox->setMinimum(0); spinBox->setMaximum(100); return spinBox; } return 0; }
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 will ensure that we set up the editor's data and geometry correctly by calling functions that we define later for this purpose.
The delegate has to provide a function to copy model data into the editor. In this example, we read the data stored under the display role, and set the value in the spin box.
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.
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(EndEditAction action, QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) { if (action == QItemDelegate::Accepted) setModelData(editor, model, index); 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 have to be made in order to use certain types of editor widgets. In this example, we have assumed that the model will always contain integer values, but we can still use this delegate with different kinds of models because QVariant will provide sensible default values for unexpected data.
It is the responsibility of the delegate to manage the editor geometry. The geometry needs to 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.
Copyright © 2004 Trolltech. | Trademarks | Qt 4.0.0-tp1 |