我已经苦恼了一段时间,但似乎找不到正确的方法来实现我的需求。
我想要的是能够使用一个动画图标作为某些项目的装饰(通常用于显示此特定项目的一些处理正在进行)。我有一个自定义表格模型,在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
的扩展,可以绘制进度条(等等)。为此,该委托使用的方法与我提出的问题非常相似,但是它存储视图和索引而不是绘图工具;在每个计时器超时时,委托更新所有视图,最好只更新需要更新的项目。 - Luc Touraille