Я уже несколько раз боролся с этим, и я не могу найти правильный способ сделать это.
Я хотел бы использовать возможность использовать анимированный значок в качестве украшения для некоторых моих предметов (как правило, чтобы показать, что для этого конкретного объекта существует некоторая обработка). У меня есть пользовательская модель таблицы, которую я показываю в 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 * >();
}