我已经为此苦苦挣扎了一段时间,我似乎找不到正确的方法来做到这一点。
我想要的是能够使用动画图标作为我的一些项目的装饰(通常表明正在对这个特定项目进行一些处理)。我有一个自定义表格模型,我显示在QTableView
.
我的第一个想法是创建一个自定义委托来处理动画的显示。当QMovie
为装饰角色传递 a 时,委托将连接到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 * >();
}