在QTableView中显示动画图标的最佳方法是什么? [英] What is the best way to display an animated icon in a QTableView?

查看:324
本文介绍了在QTableView中显示动画图标的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为此苦苦挣扎了一段时间了,但我似乎找不到正确的方法来做这件事.

I've been struggling with this for some times now, and I can't seem to find the right way to do this.

我想要的是使用动画图标作为我的某些物品的装饰的能力(通常表明该特定物品正在进行某些处理).我有一个自定义表模型,该模型显示在QTableView中.

What I would like is the ability to use an animated icon as a decoration for some of my items (typically to show that some processing is occuring for this particular item). I have a custom table model, that I display in a QTableView.

我的第一个想法是创建一个自定义委托,该委托将负责显示动画.当传递装饰角色的QMovie时,委托将连接到QMovie以便在每次有新框架可用时更新显示(请参见下面的代码).但是,在调用委托的paint方法之后,绘制程序似乎仍然无效(调用绘制程序的save方法时出现错误,可能是因为指针不再指向有效内存).

My first idea was to create a custom delegate that would take care of displaying the animation. When passed a QMovie for the decoration role, the delegate would connect to the QMovie in order to update the display every time a new frame is available (see code below). However, the painter does not seem to remain valid after the call to the delegate's paint method (I get an error when calling the painter's save method, probably because the pointer no longer points to valid memory).

另一种解决方案是,每当有新帧可用时,就发出该物品的dataChanged信号,但是1)由于不会真正改变数据,因此会引起许多不必要的开销; 2)在模型级别处理影片似乎并不干净:显示层(QTableView或委托)应负责处理新帧的显示.

Another solution would be to emit the dataChanged signal of the item every time a new frame is available, but 1) that would induce many unnecessary overhead, since the data is not really changed; 2) it does not seem really clean to handle the movie at the model level: it should be the responsibility of the display tier (QTableView or the delegate) to handle the display of new frames.

有人知道在Qt视图中显示动画的一种干净(最好是有效的)方法吗?

Does anyone know a clean (and preferably efficient) way to display animation in Qt views?

对于那些感兴趣的人,这是我开发的代表的代码(目前不起作用).

For those interested, here is the code of the delegate I developped (which does not work at the moment).

// Class that paints movie frames every time they change, using the painter
// and style options provided
class MoviePainter : public QObject
{
    Q_OBJECT

  public: // member functions
    MoviePainter( QMovie * movie, 
                  QPainter * painter, 
                  const QStyleOptionViewItem & option );

  public slots:
    void paint( ) const;

  private: // member variables
    QMovie               * movie_;
    QPainter             * painter_;
    QStyleOptionViewItem   option_;
};


MoviePainter::MoviePainter( QMovie * movie,
                            QPainter * painter,
                            const QStyleOptionViewItem & option )
  : movie_( movie ), painter_( painter ), option_( option )
{
    connect( movie, SIGNAL( frameChanged( int ) ),
             this,  SLOT( paint( ) ) );
}

void MoviePainter::paint( ) const
{
    const QPixmap & pixmap = movie_->currentPixmap();

    painter_->save();
    painter_->drawPixmap( option_.rect, pixmap );
    painter_->restore();
}

//-------------------------------------------------

//Custom delegate for handling animated decorations.
class MovieDelegate : public QStyledItemDelegate
{
    Q_OBJECT

  public: // member functions
    MovieDelegate( QObject * parent = 0 );
    ~MovieDelegate( );

    void paint( QPainter * painter, 
                const QStyleOptionViewItem & option, 
                const QModelIndex & index ) const;

  private: // member functions
    QMovie * qVariantToPointerToQMovie( const QVariant & variant ) const;

  private: // member variables
    mutable std::map< QModelIndex, detail::MoviePainter * > map_;
};

MovieDelegate::MovieDelegate( QObject * parent )
  : QStyledItemDelegate( parent )
{
}

MovieDelegate::~MovieDelegate( )
{
    typedef  std::map< QModelIndex, detail::MoviePainter * > mapType;

          mapType::iterator it = map_.begin();
    const mapType::iterator end = map_.end();

    for ( ; it != end ; ++it )
    {
        delete it->second;
    }
}

void MovieDelegate::paint( QPainter * painter, 
                           const QStyleOptionViewItem & option, 
                           const QModelIndex & index ) const
{
    QStyledItemDelegate::paint( painter, option, index );

    const QVariant & data = index.data( Qt::DecorationRole );

    QMovie * movie = qVariantToPointerToQMovie( data );

    // Search index in map
    typedef std::map< QModelIndex, detail::MoviePainter * > mapType;

    mapType::iterator it = map_.find( index );

    // if the variant is not a movie
    if ( ! movie )
    {
        // remove index from the map (if needed)
        if ( it != map_.end() )
        {
            delete it->second;
            map_.erase( it );
        }

        return;
    }

    // create new painter for the given index (if needed)
    if ( it == map_.end() )
    {
        map_.insert( mapType::value_type( 
                index, new detail::MoviePainter( movie, painter, option ) ) );
    }
}

QMovie * MovieDelegate::qVariantToPointerToQMovie( const QVariant & variant ) const
{
    if ( ! variant.canConvert< QMovie * >() ) return NULL;

    return variant.value< QMovie * >();
}

推荐答案

最好的解决方案是使用

The best solution is to use QSvgRenderer within delegate.

它非常容易实现,并且与gif不同,SVG轻巧且支持透明.

It's very easy to implement and unlike gif, SVG is lightweight and supports transparency.

    TableViewDelegate::TableViewDelegate(TableView* view, QObject* parent)
    : QStyledItemDelegate(parent), m_view(view)
{
    svg_renderer = new QSvgRenderer(QString{ ":/res/img/spinning_icon.svg" }, m_view);

    connect(svg_renderer, &QSvgRenderer::repaintNeeded,
        [this] {
        m_view->viewport()->update();
    });
}


void TableViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
    const QModelIndex& index) const
{
    QStyleOptionViewItem opt{ option };
    initStyleOption(&opt, index);

    if (index.column() == 0) {
        if (condition)
        {
            // transform bounds, otherwise fills the whole cell
            auto bounds = opt.rect;
            bounds.setWidth(28);
            bounds.moveTo(opt.rect.center().x() - bounds.width() / 2,
                opt.rect.center().y() - bounds.height() / 2);

            svg_renderer->render(painter, bounds);
        }
    }

    QStyledItemDelegate::paint(painter, opt, index);
}

这是一个不错的网站,您可以在其中生成自己的旋转图标并以SVG格式导出.

Here's a nice website where you can generate your own spinning icon and export in SVG.

这篇关于在QTableView中显示动画图标的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆