Home · All Namespaces · All Classes · Main Classes · Grouped Classes · Modules · Functions |
[Previous: Handling Selections in Item Views] [Contents] [Next: Item View Convenience Classes]
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 these functions.
Editors for delegates can be implemented either by using widgets to manage the editing process or by handling events directly. The first approach is covered later in this chapter, and it is also shown in the Spin Box Delegate example.
The Pixelator example shows how to create a custom delegate that performs specialized rendering for a table view.
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.
All the standard roles are handled by the default delegate used by the standard views. The way these are interpreted is described in the QItemDelegate documentation.
The delegate used by 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, we could easily have used QStandardItemModel instead since the custom delegate will control data entry. We construct a table view to display the contents of the model, and this will use the custom delegate for editing.
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 *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; };
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 createEditor() function is supplied with everything that the delegate needs to be able to set up a suitable widget:
QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const { QSpinBox *editor = new QSpinBox(parent); editor->setMinimum(0); editor->setMaximum(100); return editor; }
Note that we do not need to keep a pointer to the editor widget because the view takes responsibility for destroying it when it is no longer needed.
We install the delegate's default event filter on the editor to ensure that it provides the standard editing shortcuts that users expect. Additional shortcuts can be added to the editor to allow more sophisticated behavior; these are discussed in the section on Editing Hints.
The view ensures that the editor's data and geometry are set correctly by calling functions that we define later for these purposes. We can create different editors depending on the model index supplied by the view. For example, if we have a column of integers and a column of strings we could return either a QSpinBox or a QLineEdit, depending on which column is being edited.
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 QModelIndex &index) const { int value = index.model()->data(index, Qt::EditRole).toInt(); QSpinBox *spinBox = static_cast<QSpinBox*>(editor); spinBox->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 would need to cast the widget to the appropriate type before accessing its member functions.
When the user has finished editing the value in the spin box, the view asks the delegate to store the edited value in the model by calling the setModelData() function.
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QSpinBox *spinBox = static_cast<QSpinBox*>(editor); spinBox->interpretText(); int value = spinBox->value(); model->setData(index, value, Qt::EditRole); }
Since the view manages the editor widgets for the delegate, we only need to update the model with the contents of the editor supplied. In this case, we ensure that the spin box is up-to-date, and update the model with the value it contains using the index specified.
The standard QItemDelegate class informs the view when it has finished editing by emitting the closeEditor() signal. The view ensures that the editor widget is closed and destroyed. In this example, we only provide simple editing facilities, so we need never emit this signal.
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 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.
After editing, delegates should provide hints to the other components about the result of the editing process, and provide hints that will assist any subsequent editing operations. This is achieved by emitting the closeEditor() signal with a suitable hint. This is taken care of by the default QItemDelegate event filter which we installed on the spin box when it was constructed.
The behavior of the spin box could be adjusted to make it more user friendly. In the default event filter supplied by QItemDelegate, if the user hits Return to confirm their choice in the spin box, the delegate commits the value to the model and closes the spin box. We can change this behavior by installing our own event filter on the spin box, and provide editing hints that suit our needs; for example, we might emit closeEditor() with the EditNextItem hint to automatically start editing the next item in the view.
Another approach that does not require the use of an event filter is to provide our own editor widget, perhaps subclassing QSpinBox for convenience. This alternative approach would give us more control over how the editor widget behaves at the cost of writing additional code. It is usually easier to install an event filter in the delegate if you need to customize the behavior of a standard Qt editor widget.
Delegates do not have to emit these hints, but those that do not will be less integrated into applications, and will be less usable than those that emit hints to support common editing actions.
[Previous: Handling Selections in Item Views] [Contents] [Next: Item View Convenience Classes]
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) | Trademarks | Qt 4.5.1 |