效果图
引言
- 当我们想在
QGraphicsScene
显示或者说绘制一张GIF动图时,该如何处理? - Qt中的
QGraphicsItem
的paint
函数中,你不能直接绘制 GIF 动图。paint 函数是用来绘制QGraphicsItem
的当前状态的,它不支持动画。QGraphicsItem
是一个静态的对象,它不具备播放动画的能力。
显示GIF图片(方法一)
- 可以结合
QMovie
和QGraphicsItem
来展示GIF动画
- 创建一个自定义的
QGraphicsItem
。 - 使用
QMovie
来加载GIF文件,并通过信号和槽机制接受帧更新。 - 当GIF的新帧可用时,更新绘制的
QGraphicsItem
。 - QGraphicsItem的
paint()
函数将绘制来自QMovie
的当前帧。
class AnimatedGifItem : public QGraphicsItem
{
public:
AnimatedGifItem(const QString &fileName, QGraphicsItem *parent = nullptr)
: QGraphicsItem(parent), movie(new QMovie(fileName))
{
QObject::connect(movie, &QMovie::frameChanged, [this](int /*frame*/)
{
// 在每个新帧上触发重绘
this->update(); });
movie->start();
}
~AnimatedGifItem()
{
delete movie;
}
QRectF boundingRect() const override
{
return movie->currentPixmap().rect();
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
Q_UNUSED(option);
Q_UNUSED(widget);
auto pixmap = movie->currentPixmap();
QPixmap scaledPixmap = pixmap.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
painter->drawPixmap(0, 0, scaledPixmap);
}
private:
QMovie *movie;
};
- 然后只需要new出这些item并设置好位置加入到场景中即可
显示GIF图片(方法二)
- 你可以通过将
QMovie
对象作为QGraphicsProxyWidget
添加到QGraphicsScene
中来显示 GIF 动图,以QLabel
为载体。
QMovie *movie = new QMovie("C:\\Users\\KL162\\Desktop\\kk.gif", QByteArray(), nullptr);
// 获取GIF动画的原始大小
QSize movieSize = movie->scaledSize();
// 设定新的大小,保持宽高比
int scaledWidth = 200; // 或者其他你想要的宽度
QSize scaledSize = movieSize.scaled(scaledWidth, scaledWidth, Qt::KeepAspectRatio);
QGraphicsProxyWidget *proxyWidget = new QGraphicsProxyWidget();
QLabel *label = new QLabel;
label->setMovie(movie);
label->setFixedSize(scaledSize);
movie->start();
proxyWidget->setWidget(label);
proxyWidget->setPos(0, 0);
scene.addItem(proxyWidget);
GIF图片在场景中运动
- 要使图片在
QGraphicsScene
中随机移动,你可以使用QTimer
来定期更改它的位置。以下是如何实现这个功能的步骤:
- 创建一个 QTimer 对象。
- 连接 QTimer 的 timeout 信号到一个槽函数,该槽函数将移动 QGraphicsProxyWidget。
- 启动 QTimer。
QGraphicsScene scene;
scene.setSceneRect(0, 0, 600, 600);
scene.setItemIndexMethod(QGraphicsScene::NoIndex); // 设置场景的项索引方法,NoIndex表示场景不使用索引来优化项的遍历和碰撞检测。
// AnimatedGifItem *gifItem = new AnimatedGifItem("C:\\Users\\KL162\\Desktop\\kk.gif");
// AnimatedGifItem *gifItem2 = new AnimatedGifItem("C:\\Users\\KL162\\Desktop\\kk.gif");
// AnimatedGifItem *gifItem3 = new AnimatedGifItem("C:\\Users\\KL162\\Desktop\\kk.gif");
// gifItem->setPos(100, 100);
// gifItem2->setPos(400, 400);
// gifItem3->setPos(300, 300);
// scene.addItem(gifItem);
// scene.addItem(gifItem2);
// scene.addItem(gifItem3);
QMovie *movie = new QMovie("C:\\Users\\KL162\\Desktop\\kk.gif", QByteArray(), nullptr);
// 获取GIF动画的原始大小
QSize movieSize = movie->scaledSize();
// 设定新的大小,保持宽高比
int scaledWidth = 200; // 或者其他你想要的宽度
QSize scaledSize = movieSize.scaled(scaledWidth, scaledWidth, Qt::KeepAspectRatio);
QGraphicsProxyWidget *proxyWidget = new QGraphicsProxyWidget();
QLabel *label = new QLabel;
label->setMovie(movie);
label->setFixedSize(scaledSize);
movie->start();
proxyWidget->setWidget(label);
proxyWidget->setPos(0, 0);
scene.addItem(proxyWidget);
QGraphicsView *view = new QGraphicsView(&scene);
view->setRenderHint(QPainter::Antialiasing); // 设置视图的渲染提示,开启抗锯齿,以提高绘制质量
view->setCacheMode(QGraphicsView::CacheBackground); // 设置视图的缓存模式,缓存背景以提高性能。
view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); // 设置视图的视口更新模式,只有当项的边界矩形发生变化时才更新视口
view->setDragMode(QGraphicsView::ScrollHandDrag); // 设置视图的拖动模式,允许用户通过拖动来移动视图
view->resize(400, 300);
view->show();
// 创建一个 QTimer 对象
QTimer *timer = new QTimer();
// 连接 QTimer 的 timeout 信号到一个槽函数
QObject::connect(timer, &QTimer::timeout, [&]()
{
// 获取随机生成器实例
QRandomGenerator *randGen = QRandomGenerator::global();
// 随机生成新的 x 和 y 坐标
int newX = randGen->bounded(scene.width() - proxyWidget->boundingRect().width());
int newY = randGen->bounded(scene.height() - proxyWidget->boundingRect().height());
// 设置 QGraphicsProxyWidget 的新位置
proxyWidget->setPos(newX, newY); });
// 启动 QTimer
timer->start(1000); // 每秒移动一次
- 此处以方法二的为例的,使用方法一显示图片在场景中一样可移动