与其尝试将某些数据映射到某些模型项,不如让每个项都存储其数据。这涉及使用委托进行绘画,而不是依赖QAbstractItemView 绘画事件。
让我们有一个简单的有状态类来表示我们的项目数据,并使用Q_DECLARE_METATYPE 宏使其成为 Qt 元对象系统中的新公民。
#include <QMetaType>
class ItemData
{
public:
ItemData() = default;
ItemData(int d) : _data(d){}
int data() const { return _data; }
void paint(QPainter *painter, QRect rect);
private:
int _data;
};
Q_DECLARE_METATYPE(ItemData)
现在是委托,确实是一个非常简单的委托:
#include <QStyledItemDelegate>
class ItemDelegate : public QStyledItemDelegate
{
public:
// ...
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
// ...
};
委托绘制方法,是事情发生的地方:
void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.data().canConvert<ItemData>())
{
ItemData itemdata = qvariant_cast<ItemData>(index.data());
itemdata.paint(painter, option.rect);
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
在这里我们可以使用模型索引来表达它的含义:检索项目数据。不过,我们的项目可以自己绘制。让它根据它的内部状态画一些圆圈:
void ItemData::paint(QPainter *painter, QRect rect)
{
QRect r(rect.left() + 2, rect.top() + 2, rect.height() - 4, rect.height() - 4);
for(int i=0; i<_data; ++i)
{
painter->drawEllipse(r);
r.moveLeft(r.left() + rect.height() + 2);
}
}
在更灵活的设计中,数据与渲染分离,因此ItemData 类没有paint 方法,并且由委托自己执行绘制:
void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.data().canConvert<ItemData>())
{
ItemData itemdata = qvariant_cast<ItemData>(index.data());
//itemdata.paint(painter, option.rect);
QRect rect = option.rect;
QRect r(rect.left() + 2, rect.top() + 2, rect.height() - 4, rect.height() - 4);
for(int i=0; i<itemdata.data(); ++i)
{
painter->drawEllipse(r);
r.moveLeft(r.left() + rect.height() + 2);
}
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}
}
这样,我们可以为不同的视图实现并选择不同的代理,同时保持一个一致的模型并在它们之间共享。
在像QListWidget 这样的基于项目的小部件中使用委托和数据非常简单。
在表单构造函数中:
ui->listWidget->setItemDelegate(new ItemDelegate());
for(int i=0; i<10; ++i)
{
QListWidgetItem * item = new QListWidgetItem();
item->setData(0, QVariant::fromValue(ItemData(i + 1)));
ui->listWidget->addItem(item);
}
不过,在基于模型的模型中并没有太大的不同:不是将数据设置为项目,而是将数据设置为模型,再次使用QVariant::fromValue。