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