Qt:动画 QPixmap
Posted
技术标签:
【中文标题】Qt:动画 QPixmap【英文标题】:Qt: Animating QPixmap 【发布时间】:2018-05-27 07:46:03 【问题描述】:答案代码在这里(https://***.com/a/50550471/4962676):https://github.com/eyllanesc/***/tree/master/50550089
答案比下面的代码更简单——上面的代码使用 QPropertyAnimation 而不是像下面那样使用带有 QThread 的 for 循环——这样可以节省大量的代码空间并且效率更高。
原始问题如下:
我正在使用 Qt 编写应用程序,但在关闭应用程序和线程时遇到问题。
基本上,应用程序窗口会关闭,但该进程仍处于后台并且永远不会关闭。
我的主标题(只包含类,因为有很多包含):
class ChatUI : public QWidget
Q_OBJECT
public:
explicit ChatUI(QWidget *parent = 0);
~ChatUI();
private:
// The UI itself
Ui::ChatUI * ui;
// Current appliation startup directory
QString applicationStartupDir = QDir::currentPath() + "/";
// Typing indicator stuff
QFrame * typingIndicator = nullptr;
QImage circleImage;
ThreadController * typingIndicatorThread = new ThreadController(false);
bool currentlyFadingTypingIndicator = false;
// The calm before the storm
bool closing = false;
void showTypingIndicator();
void hideTypingIndicator();
signals:
void WidgetClosed();
protected:
void closeEvent(QCloseEvent*);
;
我的控制器(标题):
#ifndef THREADCONTROLLER_H
#define THREADCONTROLLER_H
#include <QObject>
#include <QThread>
#include <QImage>
#include <QFrame>
#include "typingindicatorthread.h"
class ThreadController : public QObject
Q_OBJECT
QThread * workerThread = nullptr;
TypingIndicatorThread * worker = nullptr;
signals:
void startWork(QFrame*, QImage);
//void killThread();
public slots:
void killThread();
public:
ThreadController(bool);
;
#endif // THREADCONTROLLER_H
我的控制器(来源):
#include "threadcontroller.h"
#include <QDebug>
ThreadController::ThreadController(bool asd)
if (asd == true)
workerThread = new QThread();
worker = new TypingIndicatorThread;
worker->moveToThread(workerThread);
workerThread->start();
connect(this, &ThreadController::startWork, worker, &TypingIndicatorThread::startWorker);
else
workerThread = new QThread();
workerThread->quit();
workerThread->wait();
delete workerThread;
void ThreadController::killThread()
emit worker->killSignal();
workerThread->quit();
workerThread->wait();
我的线程(标题):
#ifndef TYPINGINDICATORTHREAD_H
#define TYPINGINDICATORTHREAD_H
#include <QObject>
#include <QLabel>
#include <QPixmap>
#include <QImage>
#include <QEventLoop>
#include <QTimer>
#include <QMetaObject>
#include <QPropertyAnimation>
class TypingIndicatorThread : public QObject
Q_OBJECT
public:
~TypingIndicatorThread();
private:
bool kill = false;
public slots:
void startWorker(QFrame*, QImage);
void killSignal();
;
#endif // TYPINGINDICATORTHREAD_H
我的线程(来源):
#include "typingindicatorthread.h"
#include <QDebug>
inline void delay(int millisecondsWait)
QEventLoop loop;
QTimer t;
t.connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
t.start(millisecondsWait);
loop.exec();
void TypingIndicatorThread::startWorker(QFrame * typingIndicator, QImage circleImage)
int max = 30;
int min = 5;
int waitTime = 5;
QMetaObject::invokeMethod(typingIndicator, [=]()
QLabel * circle1 = new QLabel(typingIndicator);
circle1->setGeometry(0,0, 50, 50);
circle1->setAlignment(Qt::AlignCenter);
circle1->show();
QLabel * circle2 = new QLabel(typingIndicator);
circle2->setGeometry(40,0, 50, 50);
circle2->setAlignment(Qt::AlignCenter);
circle2->show();
QLabel * circle3 = new QLabel(typingIndicator);
circle3->setGeometry(80,0, 50, 50);
circle3->setAlignment(Qt::AlignCenter);
circle3->show();
forever
if (kill)
qDebug() << "Killing thread";
return;
// Circle1
for (int i=min; i < max; i++)
delay(waitTime);
QPixmap circlePixmap = QPixmap::fromImage(circleImage).scaled(QSize(i, i), Qt::KeepAspectRatio, Qt::SmoothTransformation);
circle1->setPixmap(circlePixmap);
for (int i=max; i > min; i--)
delay(waitTime);
QPixmap circlePixmap = QPixmap::fromImage(circleImage).scaled(QSize(i, i), Qt::KeepAspectRatio, Qt::SmoothTransformation);
circle1->setPixmap(circlePixmap);
// Circle2
for (int i=min; i < max; i++)
delay(waitTime);
QPixmap circlePixmap = QPixmap::fromImage(circleImage).scaled(QSize(i, i), Qt::KeepAspectRatio, Qt::SmoothTransformation);
circle2->setPixmap(circlePixmap);
for (int i=max; i > min; i--)
delay(waitTime);
QPixmap circlePixmap = QPixmap::fromImage(circleImage).scaled(QSize(i, i), Qt::KeepAspectRatio, Qt::SmoothTransformation);
circle2->setPixmap(circlePixmap);
// Circle3
for (int i=min; i < max; i++)
delay(waitTime);
QPixmap circlePixmap = QPixmap::fromImage(circleImage).scaled(QSize(i, i), Qt::KeepAspectRatio, Qt::SmoothTransformation);
circle3->setPixmap(circlePixmap);
for (int i=max; i > min; i--)
delay(waitTime);
QPixmap circlePixmap = QPixmap::fromImage(circleImage).scaled(QSize(i, i), Qt::KeepAspectRatio, Qt::SmoothTransformation);
circle3->setPixmap(circlePixmap);
);
void TypingIndicatorThread::killSignal()
qDebug() << "oh no we are going to the shadow realm";
kill = true;
TypingIndicatorThread::~TypingIndicatorThread()
emit killSignal();
我在动画中使用凌乱的 for 循环的唯一原因是因为我尽可能多地研究动画图像,但是在 QML 之外没有任何东西可以制作动画,并且应用程序是 c++。
如果可能的话,最好使用 QPropertyAnimation 作为替代方案,但我似乎无法在更新气泡(圆圈)大小的同时更新它显示的大小。
如果代码很多,我深表歉意,我只是想提供尽可能多的上下文(这是相关的)来帮助解决这个问题。
【问题讨论】:
你可以解释一下你想要什么样的动画,我认为这应该是你真正的问题。 我想为图像的比例(高度/宽度)设置动画 - 这样我可以让它从小到大或反转。 好的,我明白了,你可以显示文件 chatui.cpp 该文件大约有 500 行长,您想查看哪些具体部分?具体我如何使用这些类? 【参考方案1】:制作动画不需要使用QThread
,因为这个Qt提供了QPropertyAnimation
类,如果你想让动画是连续的,你必须使用QSequentialAnimationGroup
对于QLabel
,您必须将scaledContents
设置为true,以便QPixmap
与QLabel
的大小相同。
#include <QApplication>
#include <QFrame>
#include <QLabel>
#include <QSequentialAnimationGroup>
#include <QPropertyAnimation>
int main(int argc, char *argv[])
QApplication a(argc, argv);
QFrame frame;
frame.resize(320, 240);
QSequentialAnimationGroup group;
group.setLoopCount(-1);
int minSize = 5;
int maxSize = 30;
int labelSize = 50;;
for(const QPoint & pos: QPoint(0, 0), QPoint(0, 40), QPoint(0, 80))
QRect startVal = QRect(pos + (QPoint(labelSize, labelSize) + QPoint(-minSize, -minSize))/2 , QSize(minSize, minSize));
QRect endVal = QRect(pos + (QPoint(labelSize, labelSize) + QPoint(-maxSize, -maxSize))/2 , QSize(maxSize, maxSize));
QLabel *label = new QLabel(&frame);
label->setGeometry(startVal);
label->setPixmap(QPixmap(":/circle.png"));
label->setScaledContents(true);
label->setAlignment(Qt::AlignCenter);
QPropertyAnimation *animation = new QPropertyAnimation(label, "geometry");
animation->setStartValue(startVal);
animation->setKeyValueAt(.5, endVal);
animation->setEndValue(startVal);
animation->setDuration(1000);
group.addAnimation(animation);
group.start();
frame.show();
return a.exec();
您可以在以下link找到完整的代码
【讨论】:
这不会锁定 UI 线程?这样我就可以继续和它互动了? @Jacob QXXXAnimations 不会阻塞 GUI,它们对 Qt 事件循环很友好,因为它们是被创建的。 @Jacob :当然不会,因为在您的代码中没有与动画相关的额外工作 - 事件循环完成所有工作,并且 UI 保持响应。 还有一件事 - 是否可以为固定大小设置动画?我可能必须使用布局来正确定位图像。我的问题是由于标签大小的变化,图像在重新调整大小时会偏离中心。如果可能,我需要在调整大小时将其居中。 @Jacob 解释得更好,那些标签也有文字。 QLabel 只能有文本或 QPixmap,不能有 2 种东西。以上是关于Qt:动画 QPixmap的主要内容,如果未能解决你的问题,请参考以下文章