Как я могу применить графический эффект к изображению в QListView?

Я хотел бы применить некоторый графический эффект к растровому изображению элемента списка в QListView.

Что я должен сделать, чтобы достичь этого?

Насколько я понимаю, мне нужно сделать для этого собственного делегата. Но как мне использовать QGraphicsEffect в нем?

Обновить.

Если используется QListWidget, я могу сделать что-то со следующим эффектом. Создайте виджеты для каждого элемента списка и примените для них нужный QGraphicsEffect. Этот виджет будет выглядеть так (например):

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);
}

};

Но в этом случае мне придется также реализовать обновление данных на виджетах, чтобы они почти всегда отображали данные, и это очень утомительно. В настоящее время с помощью QListView изменение данных в модели просто и понятно — и я даже могу изменить используемую модель на лету ,

Есть ли способ добиться того же внешнего вида предмета? Может быть, есть шаблон реализации делегатов, который может быть применим …

0

Решение

Вдохновлен следующим вопросом: Как размыть изображение QPixmap, Я пришел к следующему решению: использовать реализацию фильтра drophadow в делегате, вместо того, чтобы пытаться использовать QGraphicsEffect там.

Итак, я пришел к такому выводу:

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);
}

Большая часть кода, который выполняет размытие, взята из QPixmapDropShadowFilter реализация.

1

Другие решения

Давайте внесем вклад в эту тему. Начиная с Qt 5.3, следующая функция вам очень поможет QGraphicsEffect в QImage (и не теряя альфа). После применения размытия добавьте это QImage в ваш контейнер, как вам нравится.

QImage applyEffectToImage(QImage src, QGraphicsEffect *effect, int extent = 0)
{
if(src.isNull()) return QImage();   //No need to do anything else!
if(!effect) return src;             //No need to do anything else!
QGraphicsScene scene;
QGraphicsPixmapItem item;
item.setPixmap(QPixmap::fromImage(src));
item.setGraphicsEffect(effect);
scene.addItem(&item);
QImage res(src.size()+QSize(extent*2, extent*2), QImage::Format_ARGB32);
res.fill(Qt::transparent);
QPainter ptr(&res);
scene.render(&ptr, QRectF(), QRectF( -extent, -extent, src.width()+extent*2, src.height()+extent*2 ) );
return res;
}

Их использование этой функции для размытия изображения очень просто:

QGraphicsBlurEffect *blur = new QGraphicsBlurEffect;
blur->setBlurRadius(8);
QImage source("://img1.png");
QImage result = applyEffectToImage(source, blur);
result.save("final.png");

Конечно, вам не нужно его сохранять, это был всего лишь пример полезности.
Вы даже можете отбросить тень:

QGraphicsDropShadowEffect *e = new QGraphicsDropShadowEffect;
e->setColor(QColor(40,40,40,245));
e->setOffset(0,10);
e->setBlurRadius(50);
QImage p("://img3.png");
QImage res = applyEffectToImage(p, e, 40);

И обратите внимание на параметр экстента, он добавляет extent количество пикселей со всех сторон исходного изображения, особенно полезно, чтобы тени и размытия не обрезались.

1

Что ж, ответ таков: я бы вообще не предлагал использовать QGraphicsEffect для класса делегата.
Дело в том, что Qt использует QGraphicsEffect в качестве конвейера между тем, как отрисовываются определенные элементы управления, и физическим графическим устройством.

Это означает, что базовый класс — QGraphicsEffect объявляет внутри себя несколько друзей, классы Qt, для которых «включены эффекты»:

class QGraphicsEffect {
....
private:
...
friend class QGraphicsItem;
friend class QGraphicsItemPrivate;
friend class QGraphicsScenePrivate;
friend class QWidget;
friend class QWidgetPrivate;
...

};

На самом деле такое объявление означает, что эти классы могут получить доступ к защищенным методам любого графического эффекта, чтобы изменить собственное поведение внутри циклов рисования. Другими словами, они передают собственный взгляд в фильтр эффектов перед тем, как рисовать себя.

Так как QAbstractItemDelegate отсутствует в этом списке, у вас проще не будет доступа к методам эффектов, даже если вы сможете получить доступ к экземпляру графического эффекта из родительского элемента управления.

Поэтому я думаю, что лучший способ (если вам нужно имитировать эффект для определенного растрового изображения) — создать собственный класс, который будет выполнять эту работу, а не использовать существующий эффект.

Очевидно, что другой вариант — применить эффект ко всему QListView, который определенным образом обработает ваши элементы, но я думаю, что это может быть довольно сложной реализацией.

0
По вопросам рекламы [email protected]