Qt中QStyledItemDelegate的使用(二)
延续上一篇文章。本文给出了一个QStyledItemDelegate类自定义绘制加自定义编辑框的例子。为方便读者理清思路,我已经尽量简化本文附加的代码了。此程序模拟用户给出星级评价的效果,在编辑状态下用户可以设置0~5个星星的评价,在普通状态下界面显示对应数量的金黄色星星。本文代码在VS2017和Qt5.9环境下测试通过。程序运行界面如下:
头文件。Qt也建议在自绘时同时重写sizeHint()函数给出一个合适的显示空间大小,这里为了简化就不重写了,影响不大:
class MStarDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit MStarDelegate(QObject *parent = 0); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; 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; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; };
CPP文件。在paint(...)函数中首先生成五角星的五个顶点坐标,然后在for循环中从左到右以一定间隔绘制指定数量的星星:
MStarDelegate::MStarDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void MStarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QPointF vertex[5]; /* 五角星 Size=20,20 */ for (int i = 0; i < 5; i++) { vertex[i].setX(+10 * sinf(i * 144.0f * (3.1415926535f / 180.0f))); vertex[i].setY(-10 * cosf(i * 144.0f * (3.1415926535f / 180.0f))); } int many = index.data(Qt::DisplayRole).toInt(); painter->setPen(Qt::NoPen); painter->setBrush(QColor(255, 170, 0)); QSize iconSize = option.decorationSize; QRect thisRect = option.rect; int y = thisRect.y() + thisRect.height() / 2; for (int i = 0; i < many; i++) { painter->save(); painter->setRenderHint(QPainter::Antialiasing); qreal left = 2 + iconSize.width() * 0.5 + (2 + iconSize.width()) * i; painter->translate(left, y); painter->scale(iconSize.width() / 20.0, iconSize.height() / 20.0); painter->drawPolygon(vertex, 5, Qt::WindingFill); painter->restore(); } } QWidget *MStarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QSpinBox* cbBox = new QSpinBox(parent); cbBox->setRange(0, 5); cbBox->setSuffix(u8"星"); return cbBox; } void MStarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QSpinBox* cbWidget = dynamic_cast<QSpinBox*>(editor); cbWidget->setValue(index.data(Qt::DisplayRole).toInt()); } void MStarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { int level = dynamic_cast<QSpinBox*>(editor)->value(); model->setData(index, level, Qt::DisplayRole); } void MStarDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { editor->setGeometry(option.rect); }
主窗口的构造函数代码如下。其中QtTest是主窗口类,ui.tvHost是QTableView控件。
QtTest::QtTest(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); ui.tvHost->setItemDelegateForColumn(0, new MStarDelegate(ui.tvHost)); QStandardItemModel* model = new QStandardItemModel(ui.tvHost); model->setHorizontalHeaderLabels({ u8"状态", u8"说明" }); model->setVerticalHeaderLabels({ u8"设备1", u8"设备2", u8"设备3", u8"设备4" }); model->setColumnCount(2); model->setRowCount(4); for (int i = 0; i < 4; i++) { model->setItem(i, 0, new QStandardItem(u8"3")); model->setItem(i, 1, new QStandardItem(u8"示例文字")); } ui.tvHost->setModel(model); }
附:上述代码在测试时候会发现,当鼠标选中星星所在单元格后,星星的背景仍是白色,而不是系统默认的蓝色背景,仿佛没有被选择一样。这个问题需要在paint(...)函数中自己绘制单元格选中时的样式。可在paint(...)函数中添加下述代码绘制蓝色背景:
void MStarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { ... if (option.state & QStyle::State_Selected) { painter->fillRect(option.rect, option.palette.highlight()); } ... }
代码中option.state代表本单元格的状态。常见的有State_Enabled表示没有被禁用;State_Selected表示被选中;State_HasFocus表示拥有键盘输入焦点,系统默认是在单元格边缘绘制虚线矩形。还有很多其它的我也不太明白是什么意思。