I would like to apply some graphic effect to the pixmap of the list item in QListView.
What should i do to achieve that?
As far as i understand, i need to make my own delegate for that. But how do i use QGraphicsEffect in it?
Update.
If QListWidget is used, i can do something to the following effect. Create widgets for every list item and apply desired QGraphicsEffect for them. This widget would go like this (for example):
class PortraitViewWidget : public QFrame
{
Q_OBJECT
public:
explicit PortraitViewWidget(QWidget* parent = nullptr)
{
auto imageView = new QWidget();
auto imageViewLayout = new QVBoxLayout();
auto imageLabel = new QLabel();
auto textLabel = new QLabel();
// test defaults
imageLabel->setPixmap(QPixmap("/Lenna.png"));
imageLabel->setScaledContents(true);
static qreal quality = 0.f;
quality += 0.1752f;
if(quality > 1.f)
quality = 1.f;
textLabel->setText(QString("%1%").arg(quality * 100.f, 0, 'f', 1));
textLabel->setAlignment(Qt::AlignCenter);
textLabel->setStyleSheet(
"QLabel {"
" background-color: white;"
" color: black;"
" font-size: 16px;"
" padding: 2px; }");
imageViewLayout->addWidget(imageLabel);
imageViewLayout->addWidget(textLabel);
imageViewLayout->setMargin(0);
imageViewLayout->setSpacing(0);
imageViewLayout->setContentsMargins(0, 0, 0, 0);
imageView->setLayout(imageViewLayout);
auto effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(55);
effect->setOffset(0.f);
effect->setColor(Qt::green);
imageView->setGraphicsEffect(effect);
imageView->setSizePolicy(
QSizePolicy::Expanding,
QSizePolicy::Expanding);
imageView->setMinimumSize(240, 320);
imageView->setMaximumSize(480, 640);
auto layout = new QVBoxLayout();
layout->addWidget(imageView);
layout->setMargin(25);
setLayout(layout);
}
};
But in this case i will have to also implement updating data on widgets to reflect contnts almost by hand, and this is thoroughly bothersome.Currently, with QListView changing data in model is simple and straightforward - and i can even change used model on the fly.
Is there a way to achieve the same outlook of the item? Maybe there is a pattern of implementing delegates that may be applicable...
Inspired by following question: How to blur QPixmap image, I came to the following solution: use implementation of dropshadow filter in the delegate, instead of trying to use QGraphicsEffect
there.
So, what I arrived at was this:
QT_BEGIN_NAMESPACE
extern Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0 );
QT_END_NAMESPACE
#define RADIUS 20
void
GalleryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if(option.decorationSize.isValid() &&
(option.decorationPosition == QStyleOptionViewItem::Top))
{
painter->save();
QPixmap decoration(index.data(Qt::DecorationRole).value<QPixmap>());
//1. paint background
painter->fillRect(option.rect, option.backgroundBrush);
//2. make image with shadow
QRect src(QPoint(0, 0), option.decorationSize);
src.translate(RADIUS, RADIUS);
QRect dst(src.adjusted(-RADIUS, -RADIUS, RADIUS, RADIUS + option.fontMetrics.height()));
QImage tmp(dst.size(), QImage::Format_ARGB32_Premultiplied);
tmp.fill(0);
QPainter tmpPainter(&tmp);
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
tmpPainter.fillRect(src.adjusted(-3, -3, 3, 3 + option.fontMetrics.height() * 1.2), Qt::white);
QRect textRectangle(RADIUS, src.bottom(),
tmp.width() - 2 * RADIUS, tmp.height() - src.bottom() - RADIUS);
tmpPainter.end();
// blur the alpha channel
QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
blurred.fill(0);
QPainter blurPainter(&blurred);
qt_blurImage(&blurPainter, tmp, RADIUS*1.5f, false, true);
blurPainter.end();
tmp = blurred;
// blacken the image...
tmpPainter.begin(&tmp);
tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
tmpPainter.fillRect(tmp.rect(),Qt::green);
tmpPainter.end();
// draw the blurred drop shadow...
painter->drawImage(option.rect.topLeft(), tmp);
// Draw the actual pixmap...
painter->drawPixmap(src.translated(option.rect.topLeft()),
decoration.scaled(src.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
//4. draw text under it
painter->fillRect(textRectangle.adjusted(0, 2, 0, -2).translated(option.rect.topLeft()), Qt::white);
painter->drawText(textRectangle.translated(option.rect.topLeft()), Qt::AlignCenter,
index.data(Qt::DisplayRole).toString());
if(option.state & QStyle::State_Selected)
{
QPen highlight(Qt::magenta, 5);
QRect border(option.rect);
border.adjust(3, 3, -3, -3);
painter->setPen(index.data(Qt::red);
painter->drawRoundedRect(border, 5.f, 5.f);
}
painter->restore();
}
else
QStyledItemDelegate::paint(painter, option, index);
}
Most of code that performs blur is taken from QPixmapDropShadowFilter implementation.