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,以便QPixmapQLabel 的大小相同。

#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的主要内容,如果未能解决你的问题,请参考以下文章

Qt炫酷动画专栏导航目录

Qt炫酷动画专栏导航目录

Qt炫酷动画专栏导航目录

Qt炫酷动画专栏导航目录

Qt动画窗口,Qt之对话框消失动画

Qt动画与Qt坐标小记