重写QListWidget/QListView的代理,以网格的形式展示Item
效果如下:
1、首先我们要设置QListWidget的ViewMode为图片模式IconMode
2、然后确定每个item的大小,重写代理的sizeHint函数
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
//假如整个QListWidget每行显示4个item,那么每个item的宽度就是整个QListWidget的宽度减去右边的间距(这里还多减了10px,一行才显示了4个,Qt Bug)
//然后除以4,得到每个item的宽度
//然后每个item以正方形为区域
//返回每个item的尺寸
//但是绘制的时候为了显示间距,在每个item的区域绘制时不绘制满,而是将矩形的左上角往内缩spacing的距离
int width = (option.widget->width()-spacing-10) / 4;
qDebug()<<"widget with:"<<option.widget->width();
qDebug()<<"item with:"<<width;
return QSize(width, width);
}
3、然后重写代理的paint函数
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing);
//每行4个 正方形
//绘制区域:将item实际矩形区域的左上角往内缩spacing的距离
QRect rect = option.rect.adjusted(spacing, spacing, 0, 0);
//绘制图像
painter->save();
QPixmap pixmap = index.data(Qt::UserRole+1).value<QPixmap>();//得到item的图片数据
QSize pixmapSize = pixmap.size();
pixmapSize.scale(rect.size(), Qt::KeepAspectRatio);
QRect scaledRect(QPoint(0, 0), pixmapSize);//得到图片等比例缩放的矩形
scaledRect.moveTopLeft(rect.topLeft());//将图片缩放后的矩形移动到和绘制局域的左上角重合
painter->drawPixmap(scaledRect.adjusted(2,2,-2,-2), pixmap);//整体往内缩2px,给边框留空间
painter->restore();
painter->save();
if (option.state.testFlag(QStyle::State_Selected))
{
//选中,绘制轮廓线
painter->setBrush(Qt::NoBrush);
QPen pen;
pen.setStyle(Qt::SolidLine);
pen.setColor(QColor("#ff0000"));
pen.setWidth(2);
painter->setPen(pen);
painter->drawRect(scaledRect);
}
painter->restore();
}
完整代码如下:
#ifndef CUSTOMDELETEGATEFORLISTVIEW_H
#define CUSTOMDELETEGATEFORLISTVIEW_H
#include <QStyledItemDelegate>
#include <QPainter>
#include<QDebug>
class CustomDeletegateForListView :public QStyledItemDelegate
{
Q_OBJECT
public:
CustomDeletegateForListView(QObject* parent = nullptr):QStyledItemDelegate(parent){}
~CustomDeletegateForListView() = default;
protected:
//重写sizeHint函数,这个函数返回的尺寸决定了每个Item的矩形局域的大小
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
//假如整个QListWidget每行显示4个item,那么每个item的宽度就是整个QListWidget的宽度减去右边的间距(这里还多减了10px,一行才显示了4个,Qt Bug)
//然后除以4,得到每个item的宽度
//然后每个item以正方形为区域
//返回每个item的尺寸
//但是绘制的时候为了显示间距,在每个item的区域绘制时不绘制满,而是将矩形的左上角往内缩spacing的距离
int width = (option.widget->width()-spacing-10) / 4;
qDebug()<<"widget with:"<<option.widget->width();
qDebug()<<"item with:"<<width;
return QSize(width, width);
}
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override
{
painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing);
//每行4个 正方形
//绘制区域:将item实际矩形区域的左上角往内缩spacing的距离
QRect rect = option.rect.adjusted(spacing, spacing, 0, 0);
//绘制图像
painter->save();
QPixmap pixmap = index.data(Qt::UserRole+1).value<QPixmap>();//得到item的图片数据
QSize pixmapSize = pixmap.size();
pixmapSize.scale(rect.size(), Qt::KeepAspectRatio);
QRect scaledRect(QPoint(0, 0), pixmapSize);//得到图片等比例缩放的矩形
scaledRect.moveTopLeft(rect.topLeft());//将图片缩放后的矩形移动到和绘制局域的左上角重合
painter->drawPixmap(scaledRect.adjusted(2,2,-2,-2), pixmap);//整体往内缩2px,给边框留空间
painter->restore();
painter->save();
if (option.state.testFlag(QStyle::State_Selected))
{
//选中,绘制轮廓线
painter->setBrush(Qt::NoBrush);
QPen pen;
pen.setStyle(Qt::SolidLine);
pen.setColor(QColor("#ff0000"));
pen.setWidth(2);
painter->setPen(pen);
painter->drawRect(scaledRect);
}
painter->restore();
}
private:
int spacing = 10;//每个网格之间的间距,这个值可以更改
};
#endif // CUSTOMDELETEGATEFORLISTVIEW_H
不支持随着QListWidget的缩放,显示的item也跟着缩放
只能在resizeEvent中将原来的delegate删除,new一个新的delegate设置进去