2010-12-05 106 views
9

我一直在爲此奮鬥一段時間,我似乎無法找到正確的方式來做到這一點。什麼是在QTableView中顯示動畫圖標的最佳方式?

我想是使用動畫圖標爲我的一些物品裝飾(通常表明一些處理正在發生的這個特殊項目)的能力。我有一個自定義表模型,我在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 * >(); 
} 
+1

我發現非常類似的東西[`QxtItemDelegate`(http://www.koders.com/cpp/fid5911A4434425C7038B6C507A5BF978C82A6294FD.aspx),擴展到`QtItemDelegate`這使繪製進度條(除其他事項外)。爲此,該代表使用與我的問題中提出的方法非常相似的方法,但它存儲視圖和索引而不是畫家;在計時器的每次超時時,委託人都會更新所有視圖,最好只是需要更新的項目。 – 2010-12-09 09:29:10

回答

5

爲了記錄在案,我結束了使用QAbstractItemView::setIndexWidget從我委託的paint方法中,插入一個QLabel顯示QMovie的項目裏(見下面的代碼)。

該解決方案工作得非常好,並保持與模型分離的顯示問題。一個缺點是顯示標籤中的新幀會導致整個項目再次被渲染,導致對代理的方法paint幾乎連續的調用...

爲了減少這些調用帶來的開銷,我嘗試了通過重新使用現有標籤(如果有)來最大限度地減少在委託中處理電影所做的工作。然而,當調整窗口大小時,這會導致奇怪的行爲:動畫被移到右邊,就好像兩個標籤並排放置一樣。

這麼好,這裏是一個可能的解決方案,隨時就如何改善它發表評論!

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

你能提供完整的代碼,你是如何編寫代表的?我發佈了`QListView`類似的問題並尋找解決方案。 http://stackoverflow.com/questions/31812979/how-to-show-animated-icon-in-qlistview?noredirect=1#comment51562370_31812979 – zar 2015-08-05 00:59:46