【问题标题】:How can I apply a graphic effect to the image in QListView?如何在 QListView 中对图像应用图形效果?
【发布时间】:2014-05-16 14:40:58
【问题描述】:

我想对 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 更改模型中的数据非常简单明了 - 我什至可以更改使用过的模型在飞行中。

有没有办法实现相同的项目外观?也许有一种实现委托的模式可能适用......

【问题讨论】:

  • 你能再具体一点吗?什么包含您的 QListView?你想定制它的外观(就像你提到的那样在代表中)?如果你想添加“效果”,你可能有一些已经在运行的代码,把它放在这里肯定会帮助我们理解这个问题。

标签: c++ qt qlistview


【解决方案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参数,它将extent像素数添加到原始图像的所有边,特别适用于阴影和模糊不被切断。

【讨论】:

    【解决方案2】:

    受到以下问题的启发:How to blur QPixmap image,我得出了以下解决方案:在委托中使用阴影过滤器的实现,而不是尝试在那里使用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 实现。

    【讨论】:

      【解决方案3】:

      嗯,答案是 - 我根本不建议将 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 应用效果,它会以某种方式处理您的项目,但我认为这可能是相当棘手的实现

      【讨论】:

      • 我需要因项目而异的效果 - 例如,颜色 - 取决于项目的数据。关于模仿效果 - 您是否建议研究 Qt 中的默认项目委托实现和 qgraphiceffect 实现?
      • 好吧,我建议你不要坚持图形效果.. 因为归根结底它只是图像处理并创建自己的实现
      • 他只需要使用 QGraphicsScene 来渲染给定的像素图和给定的效果,然后,当然,杀死效果,我发布了代码
      猜你喜欢
      • 1970-01-01
      • 2017-12-03
      • 2016-05-02
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      • 2023-03-17
      相关资源
      最近更新 更多