Qt:动画 QPixmap
Qt: Animating QPixmap
答案代码位于此处 ():
https://github.com/eyllanesc/Whosebug/tree/master/50550089
答案比下面的代码更简单 - 上面的代码使用 QPropertyAnimation 而不是像下面那样使用带有 QThread 的 for 循环 - 这在代码中节省了大量 space 并且效率更高。
原题如下:
我正在用 Qt 编写应用程序,但在关闭应用程序和线程时遇到问题。
基本上,应用程序 window 会关闭,但是该进程仍在后台并且永远不会关闭。
我的主要 header(只包含 class 因为有很多包含):
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*);
};
我的控制器(header):
#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();
}
我的帖子(header):
#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-loops 动画的唯一原因是因为我尽可能多地研究了动画图像,但是没有什么可以在 QML 之外制作动画并且应用程序是在 c++ 中.
如果可能的话,使用 QPropertyAnimation 作为替代方案会很棒,但我似乎无法在更新气泡(圆圈)大小的同时更新其显示的大小。
如果代码太多,我深表歉意,我只是想提供尽可能多的上下文(并且是相关的)来帮助解决这个问题。
不需要使用QThread
来制作动画,为此Qt提供了classQPropertyAnimation
,如果你想要动画是连续的你必须使用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
答案代码位于此处 (
https://github.com/eyllanesc/Whosebug/tree/master/50550089
答案比下面的代码更简单 - 上面的代码使用 QPropertyAnimation 而不是像下面那样使用带有 QThread 的 for 循环 - 这在代码中节省了大量 space 并且效率更高。
原题如下:
我正在用 Qt 编写应用程序,但在关闭应用程序和线程时遇到问题。
基本上,应用程序 window 会关闭,但是该进程仍在后台并且永远不会关闭。
我的主要 header(只包含 class 因为有很多包含):
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*);
};
我的控制器(header):
#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();
}
我的帖子(header):
#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-loops 动画的唯一原因是因为我尽可能多地研究了动画图像,但是没有什么可以在 QML 之外制作动画并且应用程序是在 c++ 中.
如果可能的话,使用 QPropertyAnimation 作为替代方案会很棒,但我似乎无法在更新气泡(圆圈)大小的同时更新其显示的大小。
如果代码太多,我深表歉意,我只是想提供尽可能多的上下文(并且是相关的)来帮助解决这个问题。
不需要使用QThread
来制作动画,为此Qt提供了classQPropertyAnimation
,如果你想要动画是连续的你必须使用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