Я боролся с этим уже несколько раз, и я не могу найти правильный способ сделать это.
Чего бы мне хотелось, так это возможности использовать анимированную иконку в качестве украшения для некоторых моих элементов (как правило, чтобы показать, что для этого конкретного элемента происходит какая-то обработка). У меня есть пользовательская модель таблицы, которую я отображаю в файле 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 * >();
}
QxtItemDelegate
, расширении кQtItemDelegate
, которое позволяет рисовать индикаторы выполнения ( между прочим). Для этого этот делегат использует подход, очень похожий на тот, который был предложен в моем вопросе, но он хранит представления и индексы вместо художников; при каждом тайм-ауте таймера делегат обновляет все представления, предпочтительно только те элементы, которые нуждаются в обновлении. - person Luc Touraille   schedule 09.12.2010