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

时间:2014-05-16 14:40:59

标签: c++ qt qlistview

我想将一些图形效果应用于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在模型中更改数据简单明了 - 我甚至可以更改使用的模型在飞行中。

有没有办法实现相同的项目前景?也许有一种实施代表的模式可能适用......

3 个答案:

答案 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,它以某种方式处理你的项目,但我认为这可能是非常棘手的实现