https://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html

一、介绍

star委托示例展示了如何创建一个可以自己绘制并支持编辑的委托。

当在QListView、QTableView或QTreeView中显示数据时,单个项由委托绘制。此外,当用户开始编辑项(例如,双击项)时,委托提供一个编辑器小部件,在进行编辑时,该小部件被放置在项的顶部。

委托是QAbstractItemDelegate的子类。Qt提供了QStyledItemDelegate,它继承了QAbstractItemDelegate并处理最常见的数据类型(特别是int和QString)。如果我们需要支持自定义数据类型,或者想要自定义现有数据类型的呈现或编辑,我们可以子类化QAbstractItemDelegate或QStyledItemDelegate。如果你需要Qt的模型/视图体系结构(包括委托)的高级介绍,请参阅委托类以获得更多关于委托和模型/视图编程的信息。

在本例中,我们将看到如何实现自定义委托来呈现和编辑“星级评级”数据类型,它可以存储诸如“5颗星中的1颗”这样的值。

这个例子由以下类组成:

  • StarRating:是自定义数据类型。它存储以星表示的评级,如“5星中的2星”或“6星中的5星”。
  • starDelegate:继承了QStyledItemDelegate并提供了对StarRating的支持(除了QStyledItemDelegate已经处理的数据类型之外)。
  • StarEditor继承了QWidget,StarDelegate使用它来让用户使用鼠标编辑星级评级。

为了显示StarDelegate的实际操作,我们将用一些数据填充一个QTableWidget,并在其上安装delegate。

二、StarDelegate类

2.1定义

以下是StarDelegate类的定义:

  1. class StarDelegate : public QStyledItemDelegate {
  2. Q_OBJECT
  3. public:
  4. // 使用父类的构造函数
  5. using QStyledItemDelegate::QStyledItemDelegate;
  6. void paint(QPainter* painter, const QStyleOptionViewItem& option,
  7. const QModelIndex& index) const override;
  8. QSize sizeHint(const QStyleOptionViewItem& option,
  9. const QModelIndex& index) const override;
  10. QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,
  11. const QModelIndex& index) const override;
  12. void setEditorData(QWidget* editor, const QModelIndex& index) const override;
  13. void setModelData(QWidget* editor, QAbstractItemModel* model,
  14. const QModelIndex& index) const override;
  15. private slots:
  16. void commitAndCloseEditor();
  17. };

所有公共函数都是从QStyledItemDelegate中重新实现的虚函数,以提供自定义呈现和编辑。

2.2 实现

paint() 函数从QStyledItemDelegate中重新实现,并在视图需要重绘一个项目时调用

  1. void StarDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
  2. const QModelIndex& index) const
  3. {
  4. if (index.data().canConvert<StarRating>()) {
  5. StarRating starRating = qvariant_cast<StarRating>(index.data());
  6. if (option.state & QStyle::State_Selected)
  7. painter->fillRect(option.rect, option.palette.highlight());
  8. starRating.paint(painter, option.rect, option.palette,
  9. StarRating::EditMode::ReadOnly);
  10. } else {
  11. QStyledItemDelegate::paint(painter, option, index);
  12. }
  13. }

该函数对每个项调用一次,由模型中的QModelIndex对象表示。如果存储在项目中的数据是StarRating,我们将自己绘制它;否则,我们让QStyledItemDelegate为我们绘制它。这确保了StarDelegate可以处理最常见的数据类型。

如果该项目是StarRating,那么如果该项目被选中,我们将绘制背景,并使用StarRating::paint()绘制该项目,
稍后我们将回顾这一点。

由于Q_DECLARE_METATYPE()宏出现在starrate .h中,StartRatings可以存储在QVariant中。稍后详细介绍。

当用户开始编辑条目时,createEditor()函数被调用:

  1. QWidget* StarDelegate::createEditor(QWidget* parent,
  2. const QStyleOptionViewItem& option,
  3. const QModelIndex& index) const
  4. {
  5. if (index.data().canConvert<StarRating>()) {
  6. StarEditor* editor = new StarEditor(parent);
  7. connect(editor, &StarEditor::editingFinished,
  8. this, &StarDelegate::commitAndCloseEditor);
  9. return editor;
  10. }
  11. return QStyledItemDelegate::createEditor(parent, option, index);
  12. }

如果条目是StarRating,我们创建一个StarEditor,并将它的editingFinished()信号连接到commitAndCloseEditor()槽,这样我们就可以在编辑器关闭时更新模型。

下面是commitAndCloseEditor()的实现:

  1. void StarDelegate::commitAndCloseEditor()
  2. {
  3. StarEditor* editor = qobject_cast<StarEditor*>(sender());
  4. emit commitData(editor);
  5. emit closeEditor(editor);
  6. }

当用户完成编辑时,我们发出commitData()和closeEditor()(都在QAbstractItemDelegate中声明),告诉模型有编辑过的数据,并通知视图不再需要编辑器。

setEditorData()函数在创建编辑器时调用,用来自模型的数据初始化编辑器:

  1. void StarDelegate::setEditorData(QWidget* editor,
  2. const QModelIndex& index) const
  3. {
  4. if (index.data().canConvert<StarRating>()) {
  5. StarRating starRating = qvariant_cast<StarRating>(index.data());
  6. StarEditor* starEditor = qobject_cast<StarEditor*>(editor);
  7. starEditor->setStarRating(starRating);
  8. } else {
  9. QStyledItemDelegate::setEditorData(editor, index);
  10. }
  11. }

我们只需在编辑器上调用setStarRating()。

当编辑完成时,调用setModelData()函数将数据从编辑器提交给模型:

  1. void StarDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,
  2. const QModelIndex& index) const
  3. {
  4. if (index.data().canConvert<StarRating>()) {
  5. StarEditor* starEditor = qobject_cast<StarEditor*>(editor);
  6. model->setData(index, QVariant::fromValue(starEditor->starRating()));
  7. } else {
  8. QStyledItemDelegate::setModelData(editor, model, index);
  9. }
  10. }

sizeHint()函数的作用是:返回一个元素的首选大小:

  1. QSize StarDelegate::sizeHint(const QStyleOptionViewItem& option,
  2. const QModelIndex& index) const
  3. {
  4. if (index.data().canConvert<StarRating>()) {
  5. StarRating starRating = qvariant_cast<StarRating>(index.data());
  6. return starRating.sizeHint();
  7. }
  8. return QStyledItemDelegate::sizeHint(option, index);
  9. }

三、StarEditor类

3.1 定义

在实现StarDelegate时使用了StarEditor类。下面是类的定义:

  1. class StarEditor : public QWidget {
  2. Q_OBJECT
  3. public:
  4. StarEditor(QWidget* parent = nullptr);
  5. QSize sizeHint() const override;
  6. void setStarRating(const StarRating& starRating)
  7. {
  8. myStarRating = starRating;
  9. }
  10. StarRating starRating() { return myStarRating; }
  11. signals:
  12. void editingFinished();
  13. protected:
  14. void paintEvent(QPaintEvent* event) override;
  15. void mouseMoveEvent(QMouseEvent* event) override;
  16. void mouseReleaseEvent(QMouseEvent* event) override;
  17. private:
  18. int starAtPosition(int x) const;
  19. StarRating myStarRating;
  20. };

这个类允许用户通过将鼠标移到编辑器上来编辑StarRating。当用户单击编辑器时,它会发出editingFinished()信号。

受保护的函数从QWidget重新实现,以处理鼠标和绘图事件。私有函数starAtPosition()是一个辅助函数,它返回鼠标指针下的*号。

3.2 实现

让我们从构造函数开始:

  1. StarEditor::StarEditor(QWidget* parent)
  2. : QWidget(parent)
  3. {
  4. setMouseTracking(true);
  5. setAutoFillBackground(true);
  6. }

我们在小部件上启用鼠标跟踪,这样即使用户没有按下任何鼠标按钮,我们也可以跟踪光标。我们还打开了QWidget的自动填充背景特性,以获得不透明的背景。(如果没有调用,视图的背景将通过编辑器发光。)

它的paintEvent()函数从QWidget重新实现:

  1. void StarEditor::paintEvent(QPaintEvent*)
  2. {
  3. QPainter painter(this);
  4. myStarRating.paint(&painter, rect(), palette(),
  5. StarRating::EditMode::Editable);
  6. }

我们简单地调用StarRating::paint()来绘制星星,就像我们在实现StarDelegate时所做的那样。

  1. void StarEditor::mouseMoveEvent(QMouseEvent* event)
  2. {
  3. const int star = starAtPosition(event->x());
  4. if (star != myStarRating.starCount() && star != -1) {
  5. myStarRating.setStarCount(star);
  6. update();
  7. }
  8. QWidget::mouseMoveEvent(event);
  9. }

在鼠标事件处理程序中,我们对私有数据成员myStarRating调用setStarCount()来反映当前光标的位置,并调用QWidget::update()来强制重绘。

  1. void StarEditor::mouseReleaseEvent(QMouseEvent* event)
  2. {
  3. emit editingFinished();
  4. QWidget::mouseReleaseEvent(event);
  5. }

当用户释放鼠标按钮时,我们只发出editingFinished()信号。

  1. int StarEditor::starAtPosition(int x) const
  2. {
  3. const int star = (x / (myStarRating.sizeHint().width() / myStarRating.maxStarCount())) + 1;
  4. if (star <= 0 || star > myStarRating.maxStarCount())
  5. return -1;
  6. return star;
  7. }

starAtPosition()函数使用基本的线性代数来找出光标下的星星。

sizeHint():首选的大小刚好足够绘制最大数目的星星。

  1. QSize StarEditor::sizeHint() const
  2. {
  3. return myStarRating.sizeHint();
  4. }

四、StarRating类

4.1 定义

  1. class StarRating {
  2. public:
  3. enum class EditMode { Editable,
  4. ReadOnly };
  5. explicit StarRating(int starCount = 1, int maxStarCount = 5);
  6. void paint(QPainter* painter, const QRect& rect,
  7. const QPalette& palette, EditMode mode) const;
  8. QSize sizeHint() const;
  9. int starCount() const { return myStarCount; }
  10. int maxStarCount() const { return myMaxStarCount; }
  11. void setStarCount(int starCount) { myStarCount = starCount; }
  12. void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; }
  13. private:
  14. QPolygonF starPolygon;
  15. QPolygonF diamondPolygon;
  16. int myStarCount;
  17. int myMaxStarCount;
  18. };
  19. Q_DECLARE_METATYPE(StarRating)

StarRating类用一些星星表示一个评级。除了保存数据之外,它还能够在QPaintDevice上绘制星星,在本例中,QPaintDevice是一个视图或编辑器。myStarCount成员变量存储当前评级,而myMaxStarCount存储可能的最高评级(通常为5)。

Q_DECLARE_METATYPE()宏使QVariant知道StarRating类型,使得在QVariant中存储StarRating值成为可能。

4.2 实现

构造函数初始化myStarCount和myMaxStarCount,并设置用于绘制星星和菱形的多边形:

  1. StarRating::StarRating(int starCount, int maxStarCount)
  2. : myStarCount(starCount)
  3. , myMaxStarCount(maxStarCount)
  4. {
  5. starPolygon << QPointF(1.0, 0.5);
  6. for (int i = 1; i < 5; ++i)
  7. starPolygon << QPointF(0.5 + 0.5 * std::cos(0.8 * i * 3.14),
  8. 0.5 + 0.5 * std::sin(0.8 * i * 3.14));
  9. diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4)
  10. << QPointF(0.6, 0.5) << QPointF(0.5, 0.6)
  11. << QPointF(0.4, 0.5);
  12. }

paint()函数用于绘制绘制设备上StarRating对象中的星星:

  1. void StarRating::paint(QPainter* painter, const QRect& rect,
  2. const QPalette& palette, EditMode mode) const
  3. {
  4. painter->save();
  5. painter->setRenderHint(QPainter::Antialiasing, true);
  6. painter->setPen(Qt::NoPen);
  7. painter->setBrush(mode == EditMode::Editable ? palette.highlight() : palette.windowText());
  8. const int yOffset = (rect.height() - PaintingScaleFactor) / 2;
  9. painter->translate(rect.x(), rect.y() + yOffset);
  10. painter->scale(PaintingScaleFactor, PaintingScaleFactor);
  11. for (int i = 0; i < myMaxStarCount; ++i) {
  12. if (i < myStarCount) {
  13. painter->drawPolygon(starPolygon, Qt::WindingFill);
  14. } else if (mode == EditMode::Editable) {
  15. painter->drawPolygon(diamondPolygon, Qt::WindingFill);
  16. }
  17. painter->translate(1.0, 0.0);
  18. }
  19. painter->restore();
  20. }

五、main函数

下面是程序的main()函数:

  1. int main(int argc, char* argv[])
  2. {
  3. QApplication app(argc, argv);
  4. QTableWidget tableWidget(4, 4);
  5. tableWidget.setItemDelegate(new StarDelegate);
  6. tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked
  7. | QAbstractItemView::SelectedClicked);
  8. tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows);
  9. tableWidget.setHorizontalHeaderLabels({ "Title", "Genre", "Artist", "Rating" });
  10. populateTableWidget(&tableWidget);
  11. tableWidget.resizeColumnsToContents();
  12. tableWidget.resize(500, 300);
  13. tableWidget.show();
  14. return app.exec();
  15. }

main()函数创建一个QTableWidget并在其上设置一个StarDelegate。DoubleClicked和SelectedClicked被设置为编辑触发器,这样当星型评级项目被选中时,编辑器就会随着一次单击而打开。

populateTableWidget()函数用数据填充QTableWidget:

  1. void populateTableWidget(QTableWidget* tableWidget)
  2. {
  3. static constexpr struct {
  4. const char* title;
  5. const char* genre;
  6. const char* artist;
  7. int rating;
  8. } staticData[] = {
  9. { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 },
  10. //! [1]
  11. { "Three More Foxes", "Jazz", "Maynard Ferguson", 4 },
  12. { "Sex Bomb", "Pop", "Tom Jones", 3 },
  13. { "Barbie Girl", "Pop", "Aqua", 5 },
  14. { nullptr, nullptr, nullptr, 0 }
  15. };
  16. for (int row = 0; staticData[row].title != nullptr; ++row) {
  17. QTableWidgetItem* item0 = new QTableWidgetItem(staticData[row].title);
  18. QTableWidgetItem* item1 = new QTableWidgetItem(staticData[row].genre);
  19. QTableWidgetItem* item2 = new QTableWidgetItem(staticData[row].artist);
  20. QTableWidgetItem* item3 = new QTableWidgetItem;
  21. // 调用QVariant::fromValue将StarRating转换为QVariant
  22. item3->setData(0, QVariant::fromValue(StarRating(staticData[row].rating)));
  23. tableWidget->setItem(row, 0, item0);
  24. tableWidget->setItem(row, 1, item1);
  25. tableWidget->setItem(row, 2, item2);
  26. tableWidget->setItem(row, 3, item3);
  27. }
  28. }

注意:调用QVariant::fromValue将StarRating转换为QVariant。

六、示例代码

stardelegate.h

  1. #ifndef STARDELEGATE_H
  2. #define STARDELEGATE_H
  3. #include <QStyledItemDelegate>
  4. class StarDelegate : public QStyledItemDelegate {
  5. Q_OBJECT
  6. public:
  7. using QStyledItemDelegate::QStyledItemDelegate;
  8. void paint(QPainter* painter, const QStyleOptionViewItem& option,
  9. const QModelIndex& index) const override;
  10. QSize sizeHint(const QStyleOptionViewItem& option,
  11. const QModelIndex& index) const override;
  12. QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,
  13. const QModelIndex& index) const override;
  14. void setEditorData(QWidget* editor, const QModelIndex& index) const override;
  15. void setModelData(QWidget* editor, QAbstractItemModel* model,
  16. const QModelIndex& index) const override;
  17. private slots:
  18. void commitAndCloseEditor();
  19. };
  20. #endif

stardelegate.cpp

  1. #include "stardelegate.h"
  2. #include "stareditor.h"
  3. #include "starrating.h"
  4. #include <QtWidgets>
  5. // paint()函数从QStyledItemDelegate中重新实现,并在视图需要重绘一个项目时调用
  6. void StarDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
  7. const QModelIndex& index) const
  8. {
  9. // 如果项目中的数据是StarRating,将自己绘制它;
  10. // 否则让QStyledItemDelegate去绘制它, 这样可以让StarDelegate处理常见的数据类型
  11. if (index.data().canConvert<StarRating>()) {
  12. StarRating starRating = qvariant_cast<StarRating>(index.data());
  13. // 如果该项目被选中, 我们将绘制背景, 并使用StarRating::paint()绘制该项目
  14. if (option.state & QStyle::State_Selected) {
  15. painter->fillRect(option.rect, option.palette.highlight());
  16. }
  17. starRating.paint(painter, option.rect, option.palette,
  18. StarRating::EditMode::ReadOnly);
  19. } else {
  20. QStyledItemDelegate::paint(painter, option, index);
  21. }
  22. }
  23. // 当用户开始编辑条目时,createEditor()函数被调用
  24. QWidget* StarDelegate::createEditor(QWidget* parent,
  25. const QStyleOptionViewItem& option,
  26. const QModelIndex& index) const
  27. {
  28. // 如果条目是StarRating,我们创建一个StarEditor,并绑定信号槽,以确保在编辑器关闭时更新模型
  29. if (index.data().canConvert<StarRating>()) {
  30. StarEditor* editor = new StarEditor(parent);
  31. connect(editor, &StarEditor::editingFinished,
  32. this, &StarDelegate::commitAndCloseEditor);
  33. return editor;
  34. }
  35. return QStyledItemDelegate::createEditor(parent, option, index);
  36. }
  37. // 当用户完成编辑时,我们发出commitData()和closeEditor(),告诉模型有编辑过的数据,并通知视图不再需要编辑器
  38. void StarDelegate::commitAndCloseEditor()
  39. {
  40. StarEditor* editor = qobject_cast<StarEditor*>(sender());
  41. // 以下两个信号在QAbstractItemDelegate中声明
  42. emit commitData(editor);
  43. emit closeEditor(editor);
  44. }
  45. // setEditorData()函数在创建编辑器时调用,用来自模型的数据初始化编辑器:
  46. void StarDelegate::setEditorData(QWidget* editor,
  47. const QModelIndex& index) const
  48. {
  49. if (index.data().canConvert<StarRating>()) {
  50. StarRating starRating = qvariant_cast<StarRating>(index.data());
  51. StarEditor* starEditor = qobject_cast<StarEditor*>(editor);
  52. starEditor->setStarRating(starRating);
  53. } else {
  54. QStyledItemDelegate::setEditorData(editor, index);
  55. }
  56. }
  57. // 我们只需在编辑器上调用setStarRating()。
  58. // 当编辑完成时,调用setModelData()函数将数据从编辑器提交给模型:
  59. void StarDelegate::setModelData(QWidget* editor, QAbstractItemModel* model,
  60. const QModelIndex& index) const
  61. {
  62. if (index.data().canConvert<StarRating>()) {
  63. StarEditor* starEditor = qobject_cast<StarEditor*>(editor);
  64. model->setData(index, QVariant::fromValue(starEditor->starRating()));
  65. } else {
  66. QStyledItemDelegate::setModelData(editor, model, index);
  67. }
  68. }
  69. // sizeHint()函数: 返回一个元素的首选大小
  70. QSize StarDelegate::sizeHint(const QStyleOptionViewItem& option,
  71. const QModelIndex& index) const
  72. {
  73. if (index.data().canConvert<StarRating>()) {
  74. StarRating starRating = qvariant_cast<StarRating>(index.data());
  75. return starRating.sizeHint();
  76. }
  77. return QStyledItemDelegate::sizeHint(option, index);
  78. }

stareditor.h

  1. #ifndef STAREDITOR_H
  2. #define STAREDITOR_H
  3. #include <QWidget>
  4. #include "starrating.h"
  5. // 在实现StarDelegate时使用了StarEditor类
  6. class StarEditor : public QWidget {
  7. Q_OBJECT
  8. public:
  9. StarEditor(QWidget* parent = nullptr);
  10. QSize sizeHint() const override;
  11. void setStarRating(const StarRating& starRating)
  12. {
  13. myStarRating = starRating;
  14. }
  15. StarRating starRating() { return myStarRating; }
  16. signals:
  17. void editingFinished();
  18. protected:
  19. void paintEvent(QPaintEvent* event) override;
  20. void mouseMoveEvent(QMouseEvent* event) override;
  21. void mouseReleaseEvent(QMouseEvent* event) override;
  22. private:
  23. int starAtPosition(int x) const;
  24. StarRating myStarRating;
  25. };
  26. #endif

stareditor.cpp

  1. #include "stareditor.h"
  2. #include "starrating.h"
  3. #include <QtWidgets>
  4. StarEditor::StarEditor(QWidget* parent)
  5. : QWidget(parent)
  6. {
  7. // 启用鼠标跟踪
  8. setMouseTracking(true);
  9. // QWidget的自动填充背景特性
  10. setAutoFillBackground(true);
  11. }
  12. // paint()用来绘制星星, 就像我们在实现StarDelegate时所做的那样
  13. void StarEditor::paintEvent(QPaintEvent*)
  14. {
  15. QPainter painter(this);
  16. myStarRating.paint(&painter, rect(), palette(),
  17. StarRating::EditMode::Editable);
  18. }
  19. // 通过调用StarRating类的setStarCount()来反映当前光标的位置, 并调用QWidget::update()来强制重绘
  20. void StarEditor::mouseMoveEvent(QMouseEvent* event)
  21. {
  22. const int star = starAtPosition(event->x());
  23. if (star != myStarRating.starCount() && star != -1) {
  24. myStarRating.setStarCount(star);
  25. update();
  26. }
  27. QWidget::mouseMoveEvent(event);
  28. }
  29. // 当用户释放鼠标按钮时, 发出editingFinished()信号
  30. void StarEditor::mouseReleaseEvent(QMouseEvent* event)
  31. {
  32. emit editingFinished();
  33. QWidget::mouseReleaseEvent(event);
  34. }
  35. // starAtPosition()函数使用基本的线性代数来找出光标下的星星
  36. int StarEditor::starAtPosition(int x) const
  37. {
  38. const int star = (x / (myStarRating.sizeHint().width() / myStarRating.maxStarCount())) + 1;
  39. if (star <= 0 || star > myStarRating.maxStarCount())
  40. return -1;
  41. return star;
  42. }
  43. // 首选的大小刚好足够绘制最大数目的星星
  44. QSize StarEditor::sizeHint() const
  45. {
  46. return myStarRating.sizeHint();
  47. }

starrating.h

  1. #ifndef STARRATING_H
  2. #define STARRATING_H
  3. #include <QPainter>
  4. #include <QPolygonF>
  5. #include <QSize>
  6. // StarRating类用一些星星表示一个评级, 保存数据与绘制星星
  7. class StarRating {
  8. public:
  9. enum class EditMode { Editable,
  10. ReadOnly };
  11. explicit StarRating(int starCount = 1, int maxStarCount = 5);
  12. void paint(QPainter* painter, const QRect& rect,
  13. const QPalette& palette, EditMode mode) const;
  14. QSize sizeHint() const;
  15. int starCount() const { return myStarCount; }
  16. int maxStarCount() const { return myMaxStarCount; }
  17. void setStarCount(int starCount) { myStarCount = starCount; }
  18. void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; }
  19. private:
  20. QPolygonF starPolygon;
  21. QPolygonF diamondPolygon;
  22. // 存储当前评级
  23. int myStarCount;
  24. // 存储可能的最高评级(通常为5)
  25. int myMaxStarCount;
  26. };
  27. // Q_DECLARE_METATYPE()宏使QVariant知道StarRating类型,使得在QVariant中存储StarRating值成为可能。
  28. Q_DECLARE_METATYPE(StarRating)
  29. #endif

starrating.cpp

  1. #include "starrating.h"
  2. #include <QtWidgets>
  3. #include <cmath>
  4. constexpr int PaintingScaleFactor = 20;
  5. // 构造函数初始化myStarCount和myMaxStarCount,并设置用于绘制星星和菱形的多边形:
  6. StarRating::StarRating(int starCount, int maxStarCount)
  7. : myStarCount(starCount)
  8. , myMaxStarCount(maxStarCount)
  9. {
  10. starPolygon << QPointF(1.0, 0.5);
  11. for (int i = 1; i < 5; ++i)
  12. starPolygon << QPointF(0.5 + 0.5 * std::cos(0.8 * i * 3.14),
  13. 0.5 + 0.5 * std::sin(0.8 * i * 3.14));
  14. diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4)
  15. << QPointF(0.6, 0.5) << QPointF(0.5, 0.6)
  16. << QPointF(0.4, 0.5);
  17. }
  18. // 首选的大小刚好足够绘制最大数目的星星
  19. QSize StarRating::sizeHint() const
  20. {
  21. return PaintingScaleFactor * QSize(myMaxStarCount, 1);
  22. }
  23. // paint()函数用于绘制绘制设备上StarRating对象中的星星
  24. void StarRating::paint(QPainter* painter, const QRect& rect,
  25. const QPalette& palette, EditMode mode) const
  26. {
  27. painter->save();
  28. painter->setRenderHint(QPainter::Antialiasing, true);
  29. painter->setPen(Qt::NoPen);
  30. painter->setBrush(mode == EditMode::Editable ? palette.highlight() : palette.windowText());
  31. const int yOffset = (rect.height() - PaintingScaleFactor) / 2;
  32. painter->translate(rect.x(), rect.y() + yOffset);
  33. painter->scale(PaintingScaleFactor, PaintingScaleFactor);
  34. for (int i = 0; i < myMaxStarCount; ++i) {
  35. if (i < myStarCount) {
  36. painter->drawPolygon(starPolygon, Qt::WindingFill);
  37. } else if (mode == EditMode::Editable) {
  38. painter->drawPolygon(diamondPolygon, Qt::WindingFill);
  39. }
  40. painter->translate(1.0, 0.0);
  41. }
  42. painter->restore();
  43. }

main.cpp

  1. // star委托示例展示了如何创建一个可以自己绘制并支持编辑的委托。
  2. #include "stardelegate.h"
  3. #include "stareditor.h"
  4. #include "starrating.h"
  5. #include <QApplication>
  6. #include <QTableWidget>
  7. // 用数据填充QTableWidget
  8. void populateTableWidget(QTableWidget* tableWidget)
  9. {
  10. static constexpr struct {
  11. const char* title;
  12. const char* genre;
  13. const char* artist;
  14. int rating;
  15. } staticData[] = {
  16. { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 },
  17. //! [1]
  18. { "Three More Foxes", "Jazz", "Maynard Ferguson", 4 },
  19. { "Sex Bomb", "Pop", "Tom Jones", 3 },
  20. { "Barbie Girl", "Pop", "Aqua", 5 },
  21. { nullptr, nullptr, nullptr, 0 }
  22. };
  23. for (int row = 0; staticData[row].title != nullptr; ++row) {
  24. QTableWidgetItem* item0 = new QTableWidgetItem(staticData[row].title);
  25. QTableWidgetItem* item1 = new QTableWidgetItem(staticData[row].genre);
  26. QTableWidgetItem* item2 = new QTableWidgetItem(staticData[row].artist);
  27. QTableWidgetItem* item3 = new QTableWidgetItem;
  28. // 调用QVariant::fromValue将StarRating转换为QVariant
  29. item3->setData(0, QVariant::fromValue(StarRating(staticData[row].rating)));
  30. tableWidget->setItem(row, 0, item0);
  31. tableWidget->setItem(row, 1, item1);
  32. tableWidget->setItem(row, 2, item2);
  33. tableWidget->setItem(row, 3, item3);
  34. }
  35. }
  36. int main(int argc, char* argv[])
  37. {
  38. QApplication app(argc, argv);
  39. QTableWidget tableWidget(4, 4);
  40. tableWidget.setItemDelegate(new StarDelegate);
  41. tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked
  42. | QAbstractItemView::SelectedClicked);
  43. tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows);
  44. tableWidget.setHorizontalHeaderLabels({ "Title", "Genre", "Artist", "Rating" });
  45. populateTableWidget(&tableWidget);
  46. tableWidget.resizeColumnsToContents();
  47. tableWidget.resize(500, 300);
  48. tableWidget.show();
  49. return app.exec();
  50. }

七、示例结果

image.png