12

私はこれにしばらく苦労してきましたが、これを行う正しい方法を見つけることができないようです。

私が望むのは、いくつかのアイテムの装飾としてアニメーション化されたアイコンを使用する機能です (通常、この特定のアイテムに対して何らかの処理が行われていることを示すため)。に表示するカスタム テーブル モデルがありQTableViewます。

私の最初のアイデアは、アニメーションの表示を処理するカスタム デリゲートを作成することでした。装飾ロールにが渡されるQMovieと、デリゲートは に接続してQMovie、新しいフレームが利用可能になるたびに表示を更新します (以下のコードを参照)。ただし、デリゲートのメソッドを呼び出した後、ペインタは有効なままではないようです(おそらくポインタが有効なメモリを指していないため、paintペインタのメソッドを呼び出すとエラーが発生します)。save

別の解決策はdataChanged、新しいフレームが利用可能になるたびにアイテムのシグナルを送信することですが、1) データが実際には変更されないため、多くの不要なオーバーヘッドが発生します。2) モデル レベルでムービーを処理するのはあまりきれいに見えませんQTableView。新しいフレームの表示を処理するのは、表示層 (またはデリゲート) の責任です。

Qtビューでアニメーションを表示するクリーンな(そしてできれば効率的な)方法を知っている人はいますか?


興味のある方のために、ここに私が開発したデリゲートのコードを示します (現時点では動作しません)。

// 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 * >();
}
4

5 に答える 5

6

記録のために、デリゲートQAbstractItemView::setIndexWidgetのメソッド内から使用して、アイテム内に表示を挿入することになりました(以下のコードを参照)。paintQLabelQMovie

このソリューションは非常にうまく機能し、表示の問題をモデルから切り離します。1 つの欠点は、ラベルに新しいフレームを表示するとアイテム全体が再度レンダリングされ、デリゲートのpaintメソッドがほぼ連続して呼び出されることです...

これらの呼び出しによって発生するオーバーヘッドを削減するために、既存のラベルがある場合はそれを再利用することで、デリゲートでムービーを処理するために行われる作業を最小限に抑えようとしました。ただし、ウィンドウのサイズを変更すると、奇妙な動作が発生します。2 つのラベルが並んで配置されているかのように、アニメーションが右に移動します。

さて、ここに考えられる解決策があります。それを改善する方法についてお気軽にコメントしてください!

// Declaration

#ifndef MOVIEDELEGATE_HPP
#define MOVIEDELEGATE_HPP

#include <QtCore/QModelIndex>
#include <QtGui/QStyledItemDelegate>


class QAbstractItemView;
class QMovie;


class MovieDelegate : public QStyledItemDelegate
{
    Q_OBJECT

  public: // member functions

    MovieDelegate( QAbstractItemView & view, QObject * parent = NULL );

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


  private: // member functions

    QMovie * qVariantToPointerToQMovie( const QVariant & variant ) const;


  private: // member variables

    mutable QAbstractItemView & view_;
};

#endif // MOVIEDELEGATE_HPP


// Definition

#include "movieDelegate.hpp"

#include <QtCore/QVariant>
#include <QtGui/QAbstractItemView>
#include <QtGui/QLabel>
#include <QtGui/QMovie>


Q_DECLARE_METATYPE( QMovie * )


//---------------------------------------------------------
// Public member functions
//---------------------------------------------------------

MovieDelegate::MovieDelegate( QAbstractItemView & view, QObject * parent )
  : QStyledItemDelegate( parent ), view_( view )
{
}


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 );

    if ( ! movie )
    {
        view_.setIndexWidget( index, NULL );
    }
    else
    {
        QObject * indexWidget = view_.indexWidget( index );
        QLabel  * movieLabel  = qobject_cast< QLabel * >( indexWidget );

        if ( movieLabel )
        {
            // Reuse existing label

            if ( movieLabel->movie() != movie )
            {
                movieLabel->setMovie( movie );
            }
        }
        else
        {
            // Create new label;

            movieLabel = new QLabel;

            movieLabel->setMovie( movie );

            view_.setIndexWidget( index, movieLabel );
        }
    }
}


//---------------------------------------------------------
// Private member functions
//---------------------------------------------------------

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

    return variant.value< QMovie * >();
}
于 2010-12-09T09:22:52.900 に答える
4

私のアプリケーションでは、テーブル内のいくつかのセルの待機/処理状態を示す典型的な回転する円のアイコンがあります。しかし、私は現在受け入れられている回答で提案されているものとは異なるアプローチを使用することになりました。QAbstractItemView::setIndexWidget)。ウィジェットの使用はやり過ぎのようで、ウィジェットが多すぎるとパフォーマンスが低下します。QAbstractItemModel私のソリューションのすべての機能は、モデル レイヤー (の子孫) クラスにのみ実装されています。ビューやデリゲートを変更する必要はありません。ただし、1 つの GIF のみをアニメーション化しており、すべてのアニメーションが同期されています。これは、私の単純なアプローチの現在の制限です。

この動作を実装するために使用されるモデル クラスには、次のものが必要です。

  • sのベクトルQImage-QImageReaderすべてのアニメーション フレームを読み取ることができる を使用し、それらをQVector<QImage>

  • アニメーション GIF の周期性を伴うQTimerカチカチ音 - 期間は を使用して取得されQImageReader::nextImageDelay()ます。

  • 現在のフレームのインデックス (int) (アニメーション化されたすべてのセルでフレームが同じであると仮定します。それらは同期されています。非同期にしたい場合は、それぞれに整数オフセットを使用できます)

  • どのセルをアニメーション化する必要があるかについてのある程度の知識と、セルを変換する機能QModelIndex(これは、特定のニーズに応じて、これを実装するためのカスタム コード次第です)

  • QAbstractItemModel::data()モデルの一部をオーバーライドしQt::DecorationRoleて、アニメーション化されたセル ( QModelIndex) に応答し、現在のフレームをQImage

  • QTimer::timeoutシグナルによってトリガーされるスロット

重要な部分は、タイマーに反応するスロットです。これを行う必要があります。

  1. 現在のフレームを増やします。m_currentFrame = (m_currentFrame + 1) % m_frameImages.size();

  2. QModelIndexList getAnimatedIndices();アニメーション化する必要があるセルのインデックス (例: ) のリストを取得します。このコードのgetAnimatedIndices()開発はあなた次第です - モデル内のすべてのセルをクエリするブルートフォースを使用するか、巧妙な最適化を使用してください...

  3. アニメーションdataChanged()化されたセルごとに信号を送信します。for (const QModelIndex &idx : getAnimatedIndices()) emit dataChanged(idx, idx, {Qt::DecorationRole});

それで全部です。どのインデックスがアニメーション化されるかを決定するための関数の複雑さに応じて、ビューを変更したりデリゲートしたりする必要なく、モデルだけで実装全体が 15 ~ 25 行程度になる可能性があると推定されます。

于 2019-03-25T09:53:24.100 に答える