如何在QListView中对图像应用图形效果?

How can I apply a graphic effect to the image in QListView?

本文关键字:图形 应用 图像 QListView      更新时间:2023-10-16

我想在QListView中对列表项的像素图应用一些图形效果。

我该怎么做才能达到这个目标?

据我所知,我需要为此创建自己的委托。但我如何使用qgraphicseeffect在它吗?

更新。

如果使用QListWidget,我可以做一些事情达到以下效果。为每个列表项创建小部件,并为它们应用所需的qgraphicseeffect。这个小部件将像这样运行(例如):

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更改模型中的数据是简单而直接的-我甚至可以动态更改使用的模型。

是否有一种方法可以实现项目的相同外观?也许有一种实现委托的模式可以适用…

受到以下问题的启发:如何模糊QPixmap图像,我想到了以下解决方案:在委托中使用dropshadow filter的实现,而不是尝试使用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的实现。

让我们为这个话题做出贡献。在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数量的像素,对于阴影和模糊不被切断特别有用。

好吧,答案是-我不建议使用qgraphicseeffect委托类。重点是Qt使用qgraphicseeffect作为一个管道,连接如何绘制特定的控件和物理图形设备。

这意味着基类- QGraphicsEffect在自己内部声明了几个朋友,Qt类是"启用效果"的:

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

};

实际上,这样的声明意味着这些类能够访问任何图形效果的保护方法来修改自己在绘图循环中的行为。换句话说,他们在绘制自己之前将自己的外观传递给效果过滤器。

由于QAbstractItemDelegate不在此列表中,因此即使您能够从父控件访问图形效果实例,您也无法访问效果方法。

所以,我认为最好的方法(如果你需要模仿特定像素图的效果)是创建自己的类,而不是使用现有的效果。

另一个选择显然是对整个QListView应用一个效果,它会以某种方式处理你的项目,但我认为这可能是相当棘手的实现