The Star Delegate example shows how to create a delegate that can paint itself and that supports editing.
						 
					
When displaying data in a QListView , QTableView ,或 QTreeView , the individual items are drawn by a delegate . Also, when the user starts editing an item (for example, by double-clicking the item), the delegate provides an editor widget that is placed on top of the item while editing takes place.
						Delegates are subclasses of
						
							QAbstractItemDelegate
						
						. Qt provides
						
							QItemDelegate
						
						, which inherits
						
							QAbstractItemDelegate
						
						and handles the most common data types (notably
						
int
						
						and
						
							QString
						
						). If we need to support custom data types, or want to customize the rendering or the editing for existing data types, we can subclass
						
							QAbstractItemDelegate
						
						or
						
							QItemDelegate
						
						。见
						
							委托类
						
						for more information about delegates, and
						
							模型/视图编程
						
						if you need a high-level introduction to Qt's model/view architecture (including delegates).
					
In this example, we will see how to implement a custom delegate to render and edit a "star rating" data type, which can store values such as "1 out of 5 stars".
范例由以下类组成:
StarRating
							
							is the custom data type. It stores a rating expressed as stars, such as "2 out of 5 stars" or "5 out of 6 stars".
						
StarDelegate
							
							继承
							
								QItemDelegate
							
							and provides support for
							
StarRating
							
							(in addition to the data types already handled by
							
								QItemDelegate
							
							).
						
StarEditor
							
							继承
							
								QWidget
							
							and is used by
							
StarDelegate
							
							to let the user edit a star rating using the mouse.
						
						To show the
						
StarDelegate
						
						in action, we will fill a
						
							QTableWidget
						
						with some data and install the delegate on it.
					
						这里是定义为
						
StarDelegate
						
						类:
					
class StarDelegate : public QStyledItemDelegate { Q_OBJECT public: StarDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; private slots: void commitAndCloseEditor(); };
All public functions are reimplemented virtual functions from QItemDelegate to provide custom rendering and editing.
The paint() function is reimplemented from QItemDelegate and is called whenever the view needs to repaint an item:
void StarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); starRating.paint(painter, option.rect, option.palette, StarRating::ReadOnly); } else { QStyledItemDelegate::paint(painter, option, index); }
						The function is invoked once for each item, represented by a
						
							QModelIndex
						
						object from the model. If the data stored in the item is a
						
StarRating
						
						, we paint it ourselves; otherwise, we let
						
							QItemDelegate
						
						paint it for us. This ensures that the
						
StarDelegate
						
						can handle the most common data types.
					
						If the item is a
						
StarRating
						
						, we draw the background if the item is selected, and we draw the item using
						
StarRating::paint()
						
						, which we will review later.
					
						
StartRating
						
						s can be stored in a
						
							QVariant
						
						thanks to the
						
							Q_DECLARE_METATYPE
						
						() macro appearing in
						
starrating.h
						
						. More on this later.
					
The createEditor() function is called when the user starts editing an item:
QWidget *StarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *editor = new StarEditor(parent); connect(editor, &StarEditor::editingFinished, this, &StarDelegate::commitAndCloseEditor); return editor; } else { return QStyledItemDelegate::createEditor(parent, option, index); } }
						If the item is a
						
StarRating
						
						,创建
						
StarEditor
						
						和连接它的
						
editingFinished()
						
						signal to our
						
commitAndCloseEditor()
						
						slot, so we can update the model when the editor closes.
					
						Here's the implementation of
						
commitAndCloseEditor()
						
						:
					
void StarDelegate::commitAndCloseEditor() { StarEditor *editor = qobject_cast<StarEditor *>(sender()); emit commitData(editor); emit closeEditor(editor); }
When the user is done editing, we emit commitData() and closeEditor() (both declared in QAbstractItemDelegate ), to tell the model that there is edited data and to inform the view that the editor is no longer needed.
The setEditorData() function is called when an editor is created to initialize it with data from the model:
void StarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); StarEditor *starEditor = qobject_cast<StarEditor *>(editor); starEditor->setStarRating(starRating); } else { QStyledItemDelegate::setEditorData(editor, index); } }
						We simply call
						
setStarRating()
						
						on the editor.
					
The setModelData() function is called to commit data from the editor to the model when editing is finished:
void StarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *starEditor = qobject_cast<StarEditor *>(editor); model->setData(index, QVariant::fromValue(starEditor->starRating())); } else { QStyledItemDelegate::setModelData(editor, model, index); } }
						The
						
sizeHint()
						
						function returns an item's preferred size:
					
QSize StarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); return starRating.sizeHint(); } else { return QStyledItemDelegate::sizeHint(option, index); } }
						We simply forward the call to
						
StarRating
						
						.
					
						The
						
StarEditor
						
						class was used when implementing
						
StarDelegate
						
						. Here's the class definition:
					
class StarEditor : public QWidget { Q_OBJECT public: StarEditor(QWidget *parent = 0); QSize sizeHint() const override; void setStarRating(const StarRating &starRating) { myStarRating = starRating; } StarRating starRating() { return myStarRating; } signals: void editingFinished(); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: int starAtPosition(int x); StarRating myStarRating; };
						The class lets the user edit a
						
StarRating
						
						by moving the mouse over the editor. It emits the
						
editingFinished()
						
						signal when the user clicks on the editor.
					
						The protected functions are reimplemented from
						
							QWidget
						
						to handle mouse and paint events. The private function
						
starAtPosition()
						
						is a helper function that returns the number of the star under the mouse pointer.
					
Let's start with the constructor:
StarEditor::StarEditor(QWidget *parent) : QWidget(parent) { setMouseTracking(true); setAutoFillBackground(true); }
We enable mouse tracking on the widget so we can follow the cursor even when the user doesn't hold down any mouse button. We also turn on QWidget 's auto-fill background feature to obtain an opaque background. (Without the call, the view's background would shine through the editor.)
The paintEvent() function is reimplemented from QWidget :
void StarEditor::paintEvent(QPaintEvent *) { QPainter painter(this); myStarRating.paint(&painter, rect(), this->palette(), StarRating::Editable); }
						We simply call
						
StarRating::paint()
						
						to draw the stars, just like we did when implementing
						
StarDelegate
						
						.
					
void StarEditor::mouseMoveEvent(QMouseEvent *event) { int star = starAtPosition(event->x()); if (star != myStarRating.starCount() && star != -1) { myStarRating.setStarCount(star); update(); } }
						In the mouse event handler, we call
						
setStarCount()
						
						on the private data member
						
myStarRating
						
						to reflect the current cursor position, and we call
						
							QWidget::update
						
						() to force a repaint.
					
void StarEditor::mouseReleaseEvent(QMouseEvent * /* event */) { emit editingFinished(); }
						When the user releases a mouse button, we simply emit the
						
editingFinished()
						
						信号。
					
int StarEditor::starAtPosition(int x) { int star = (x / (myStarRating.sizeHint().width() / myStarRating.maxStarCount())) + 1; if (star <= 0 || star > myStarRating.maxStarCount()) return -1; return star; }
						The
						
starAtPosition()
						
						function uses basic linear algebra to find out which star is under the cursor.
					
class StarRating { public: enum EditMode { Editable, ReadOnly }; explicit StarRating(int starCount = 1, int maxStarCount = 5); void paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const; QSize sizeHint() const; int starCount() const { return myStarCount; } int maxStarCount() const { return myMaxStarCount; } void setStarCount(int starCount) { myStarCount = starCount; } void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; } private: QPolygonF starPolygon; QPolygonF diamondPolygon; int myStarCount; int myMaxStarCount; }; Q_DECLARE_METATYPE(StarRating)
						The
						
StarRating
						
						class represents a rating as a number of stars. In addition to holding the data, it is also capable of painting the stars on a
						
							QPaintDevice
						
						, which in this example is either a view or an editor. The
						
myStarCount
						
						member variable stores the current rating, and
						
myMaxStarCount
						
						stores the highest possible rating (typically 5).
					
						The
						
Q_DECLARE_METATYPE()
						
						macro makes the type
						
StarRating
						
						知道
						
							QVariant
						
						, making it possible to store
						
StarRating
						
						values in
						
							QVariant
						
						.
					
						The constructor initializes
						
myStarCount
						
						and
						
myMaxStarCount
						
						, and sets up the polygons used to draw stars and diamonds:
					
StarRating::StarRating(int starCount, int maxStarCount) { myStarCount = starCount; myMaxStarCount = maxStarCount; starPolygon << QPointF(1.0, 0.5); for (int i = 1; i < 5; ++i) starPolygon << QPointF(0.5 + 0.5 * std::cos(0.8 * i * 3.14), 0.5 + 0.5 * std::sin(0.8 * i * 3.14)); diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4) << QPointF(0.6, 0.5) << QPointF(0.5, 0.6) << QPointF(0.4, 0.5); }
						The
						
paint()
						
						function paints the stars in this
						
StarRating
						
						object on a paint device:
					
void StarRating::paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(Qt::NoPen); if (mode == Editable) { painter->setBrush(palette.highlight()); } else { painter->setBrush(palette.foreground()); } int yOffset = (rect.height() - PaintingScaleFactor) / 2; painter->translate(rect.x(), rect.y() + yOffset); painter->scale(PaintingScaleFactor, PaintingScaleFactor); for (int i = 0; i < myMaxStarCount; ++i) { if (i < myStarCount) { painter->drawPolygon(starPolygon, Qt::WindingFill); } else if (mode == Editable) { painter->drawPolygon(diamondPolygon, Qt::WindingFill); } painter->translate(1.0, 0.0); } painter->restore(); }
						We first set the pen and brush we will use for painting. The
						
mode
						
						parameter can be either
						
Editable
						
						or
						
ReadOnly
						
						。若
						
mode
						
						is editable, we use the
						
							Highlight
						
						color instead of the
						
							Foreground
						
						color to draw the stars.
					
						Then we draw the stars. If we are in
						
编辑
						
						mode, we paint diamonds in place of stars if the rating is less than the highest rating.
					
						The
						
sizeHint()
						
						function returns the preferred size for an area to paint the stars on:
					
QSize StarRating::sizeHint() const { return PaintingScaleFactor * QSize(myMaxStarCount, 1); }
						The preferred size is just enough to paint the maximum number of stars. The function is called by both
						
StarDelegate::sizeHint()
						
						and
						
StarEditor::sizeHint()
						
						.
					
main()
						
						函数
						
					
						Here's the program's
						
main()
						
						函数:
					
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTableWidget tableWidget(4, 4); tableWidget.setItemDelegate(new StarDelegate); tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked); tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows); QStringList headerLabels; headerLabels << "Title" << "Genre" << "Artist" << "Rating"; tableWidget.setHorizontalHeaderLabels(headerLabels); populateTableWidget(&tableWidget); tableWidget.resizeColumnsToContents(); tableWidget.resize(500, 300); tableWidget.show(); return app.exec(); }
						The
						
main()
						
						函数创建
						
							QTableWidget
						
						and sets a
						
StarDelegate
						
						on it.
						
							DoubleClicked
						
						and
						
							SelectedClicked
						
						are set as
						
							edit triggers
						
						, so that the editor is opened with a single click when the star rating item is selected.
					
						The
						
populateTableWidget()
						
						function fills the
						
							QTableWidget
						
						with data:
					
void populateTableWidget(QTableWidget *tableWidget) { static const struct { const char *title; const char *genre; const char *artist; int rating; } staticData[] = { { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 }, ... { 0, 0, 0, 0 } }; for (int row = 0; staticData[row].title != 0; ++row) { QTableWidgetItem *item0 = new QTableWidgetItem(staticData[row].title); QTableWidgetItem *item1 = new QTableWidgetItem(staticData[row].genre); QTableWidgetItem *item2 = new QTableWidgetItem(staticData[row].artist); QTableWidgetItem *item3 = new QTableWidgetItem; item3->setData(0, QVariant::fromValue(StarRating(staticData[row].rating))); tableWidget->setItem(row, 0, item0); tableWidget->setItem(row, 1, item1); tableWidget->setItem(row, 2, item2); tableWidget->setItem(row, 3, item3); } }
						Notice the call to qVariantFromValue to convert a
						
StarRating
						
						到
						
							QVariant
						
						.
					
There are many ways to customize Qt's 模型/视图框架 . The approach used in this example is appropriate for most custom delegates and editors. Examples of possibilities not used by the star delegate and star editor are:
文件: