我想将一些图形效果应用于QListView中列表项的pixmap。
我该怎么做才能实现这一目标?
据我所知,我需要为此制作自己的代表。但是我如何在其中使用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 :(得分:1)
受到以下问题的启发:How to blur QPixmap image,我提出了以下解决方案:在委托中使用dropshadow过滤器的实现,而不是尝试在那里使用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 :(得分:1)
让我们为这个话题做出贡献。从Qt 5.3开始,以下函数将帮助您将QGraphicsEffect
应用于QImage
(并且不会丢失alpha)。应用模糊后,根据需要将此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
个像素数,尤其适用于阴影和模糊不能被截断。
答案 2 :(得分:0)
嗯,答案是 - 我根本不建议将QGraphicsEffect用于委托类。 重点是Qt使用QGraphicsEffect作为绘制某些控件和物理图形设备之间的管道。
这意味着基类 - QGraphicsEffect在其自身内部声明了几个朋友,Qt类已启用了&#39;效果&#39; :
class QGraphicsEffect {
....
private:
...
friend class QGraphicsItem;
friend class QGraphicsItemPrivate;
friend class QGraphicsScenePrivate;
friend class QWidget;
friend class QWidgetPrivate;
...
};
实际上,这样的声明意味着这些类能够访问任何图形效果的受保护方法,以修改绘制循环内的自身行为。换句话说,他们在绘制之前将自己的内容传递给效果过滤器。
由于QAbstractItemDelegate不在此列表中,即使您能够从父控件访问图形效果实例,您也无法访问效果方法。
所以,我认为最好的方法(如果你需要模仿特定pixmap的效果)就是创建自己的类来完成工作,而不是使用现有的效果。
另一个选择显然是将效果应用于整个QListView,它以某种方式处理你的项目,但我认为这可能是非常棘手的实现