Qt: Анимация QPixmap
Код ответа находится здесь ( /questions/2119469/qt-animatsiya-qpixmap/2119480#2119480):
https://github.com/eyllanesc/stackru/tree/master/50550089
Ответ был более простым, чем приведенный ниже код - в приведенном выше коде используется QPropertyAnimation, а не цикл for с QThread, как показано ниже, - который экономит тонны пространства в коде и является более эффективным.
Оригинальный вопрос ниже:
Я пишу приложение в 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 в качестве альтернативы, но я не могу анимировать размер пузырьков (кругов), одновременно обновляя отображаемый размер.
Я прошу прощения, если это много кода, я просто хотел дать как можно больше контекста (и это важно), чтобы помочь решить эту проблему.
1 ответ
Там нет необходимости использовать QThread
сделать анимацию, для этого Qt предлагает класс QPropertyAnimation
и если вы хотите, чтобы анимация была последовательной, вы должны использовать QSequentialAnimationGroup
В случае QLabel
Вы должны установить scaledContents
правда, так что 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();
}
Вы можете найти полный код по следующей ссылке