QPropertyAnimation not responding properly
-
Hi. I am trying to create a popup, that should stay there for 1500 ms and the fade away in 2500 ms. The popup itself is a pixmap.
I activate the pupup with a keypress. The first time I hit the key it appears as it should, but when I rehit it is delayed, and then it looks like it speeds up the rest of the process to finish the sequence in time. And sometimes it doesn't appear at all.What am I doing wrong.
This is my code
#include "ScreenLockOverlay.h" #include <QTimer> #include <QGraphicsOpacityEffect> #include <QPropertyAnimation> #include "Resources/PanelControl.h" #include "Widgets/ButtonWidget.h" #include <QShortcut> #include <QDebug> #define SCREEN_LOCK_DELAY 60000 #define POPUP_DELAY 1500 #define FADEOUT_TIME 2500 ScreenLockOverlay::ScreenLockOverlay(QWidget* parent) : QLabel(parent), parent(parent), locked(false) { setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::Tool); setPixmap(Pixmap::pixmap(Pixmap::LOCK_OVERLAY)); setAttribute(Qt::WA_TranslucentBackground); showTimer = new QTimer(this); connect(showTimer, &QTimer::timeout, this, &ScreenLockOverlay::fadeOut); autoLock = new QTimer(this); connect(autoLock, &QTimer::timeout, this, &ScreenLockOverlay::popup); autoLock->start(SCREEN_LOCK_DELAY); connect(new QShortcut(Qt::Key_L, parent), &QShortcut::activated, this, &ScreenLockOverlay::popup); effect = new QGraphicsOpacityEffect(this); this->setGraphicsEffect(effect); hide(); } void ScreenLockOverlay::swipe(bool state) { if (!locked) return; popup(); if (state) { locked = false; } else { } } void ScreenLockOverlay::popup() { QPoint start = parent->mapToGlobal(QPoint(0,0)); move(start.x() + parent->width()/2 - width()/2, start.y() + parent->height() - height()); show(); showTimer->start(POPUP_DELAY); locked = true; } void ScreenLockOverlay::fadeIn() { animation = new QPropertyAnimation(effect, "opacity"); animation->setDuration(FADEOUT_TIME); animation->setStartValue(0); animation->setEndValue(1); animation->setEasingCurve(QEasingCurve::InBack); animation->start(QPropertyAnimation::DeleteWhenStopped); } void ScreenLockOverlay::fadeOut() { showTimer->stop(); animation = new QPropertyAnimation(effect, "opacity"); animation->setDuration(FADEOUT_TIME); animation->setStartValue(1); animation->setEndValue(0); animation->setEasingCurve(QEasingCurve::OutBack); animation->start(QPropertyAnimation::DeleteWhenStopped); connect(animation, &QPropertyAnimation::finished, this, [=] () { hide(); qDebug() << "done"; }); }
-
Same result in release version.
-
Same result.
-
Ok, I ran this minimal example and I think I know what is going on:
#include <QApplication> #include <QTimer> #include <QGraphicsOpacityEffect> #include <QPropertyAnimation> #include <QShortcut> #include <QLabel> #include <QDebug> #include <QPixmap> #define SCREEN_LOCK_DELAY 60000 #define POPUP_DELAY 1500 #define FADEOUT_TIME 2500 class ScreenLockOverlay : public QLabel{ public: ScreenLockOverlay(QWidget* parent); private: void popup(); void fadeIn(); void fadeOut(); QTimer* showTimer; QTimer* autoLock; QGraphicsOpacityEffect* effect; QPropertyAnimation* inAnim; QPropertyAnimation* outAnim; }; ScreenLockOverlay::ScreenLockOverlay(QWidget* parent) : QLabel(parent) { setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::Tool); QPixmap overlayPix(100,100); overlayPix.fill(Qt::blue); setPixmap(overlayPix); setAttribute(Qt::WA_TranslucentBackground); showTimer = new QTimer(this); connect(showTimer, &QTimer::timeout, this, &ScreenLockOverlay::fadeOut); autoLock = new QTimer(this); connect(autoLock, &QTimer::timeout, this, &ScreenLockOverlay::popup); //autoLock->start(SCREEN_LOCK_DELAY); connect(new QShortcut(Qt::Key_L, parent), &QShortcut::activated, this, &ScreenLockOverlay::popup); effect = new QGraphicsOpacityEffect(this); this->setGraphicsEffect(effect); inAnim = new QPropertyAnimation(effect, "opacity"); inAnim->setDuration(FADEOUT_TIME); inAnim->setStartValue(0); inAnim->setEndValue(1); inAnim->setEasingCurve(QEasingCurve::InBack); outAnim = new QPropertyAnimation(effect, "opacity"); outAnim->setDuration(FADEOUT_TIME); outAnim->setStartValue(1); outAnim->setEndValue(0); outAnim->setEasingCurve(QEasingCurve::OutBack); connect(outAnim, &QPropertyAnimation::finished, this, [=] () { hide(); qDebug() << "done"; }); hide(); } void ScreenLockOverlay::popup() { Q_ASSERT(parent()); const QWidget* const parentWid = static_cast<const QWidget*>(parent()); QPoint start = parentWid->mapToGlobal(QPoint(0,0)); move(start.x() + parentWid->width()/2 - width()/2, start.y() + parentWid->height() - height()); show(); showTimer->start(POPUP_DELAY); } void ScreenLockOverlay::fadeIn() { inAnim->start(); } void ScreenLockOverlay::fadeOut() { showTimer->stop(); outAnim->start(); } int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget wid; ScreenLockOverlay *lock = new ScreenLockOverlay(&wid); wid.show(); return a.exec(); }
It's due to you using
QEasingCurve::OutBack
andQEasingCurve::InBack
which make little sense when operating opacity (opacity can never go above 100% or below 0%). The period when the animation goes above 1 or below 0 you interpret as "delay".
Regarding the "And sometimes it doesn't appear at all" you need to make sure the 2 animations are not running at the same time. At the moment if you start thefadeIn
before thefadeOut
fully completed theshow()
will almost immediately be overwritten by thehide
.So to fix this:
- use a easing curve that actually make sense (e.g.
QEasingCurve::Linear
) - before starting a
fadeIn
/fadeOut
you should check if another animation is running and amend accordingly
- use a easing curve that actually make sense (e.g.
-
@VRonin said in QPropertyAnimation not responding properly:
Linear
That makes sense.
Something changed - when using a linear curve - but still not good.
I guess that when the finished() signal is emitted, then I can start a new animation?
Should I reuse the QPropertyAnimation instead of using QPropertyAnimation::DeleteWhenStopped? -
@VRonin said in QPropertyAnimation not responding properly:
Linear
That makes sense.
Something changed - when using a linear curve - but still not good.
I guess that when the finished() signal is emitted, then I can start a new animation?
Should I reuse the QPropertyAnimation instead of using QPropertyAnimation::DeleteWhenStopped?@Jakob-Clausen said in QPropertyAnimation not responding properly:
Should I reuse the QPropertyAnimation instead of using QPropertyAnimation::DeleteWhenStopped?
I would, no need to reallocate every time.
Something changed - when using a linear curve - but still not good.
Did you address the second point?
before starting a fadeIn/fadeOut you should check if another animation is running and amend accordingly
-
Yes. I only did it manually. I start the next animation with the hit of a key, when i get "done" in the output window.
I will automate it, but could that be the problem? -
Yes. I only did it manually. I start the next animation with the hit of a key, when i get "done" in the output window.
I will automate it, but could that be the problem?@Jakob-Clausen said in QPropertyAnimation not responding properly:
but could that be the problem?
It is, as mentioned above
-
Ok. Now the code looks like this.
I have prevented i from getting started, when something is running.#include "ScreenLockOverlay.h" #include <QTimer> #include <QGraphicsOpacityEffect> #include <QPropertyAnimation> #include "Resources/PanelControl.h" #include "Widgets/ButtonWidget.h" #include <QShortcut> #include <QDebug> #define SCREEN_LOCK_DELAY 60000 #define POPUP_DELAY 1500 #define FADEOUT_TIME 2500 ScreenLockOverlay::ScreenLockOverlay(QWidget* parent) : QLabel(parent), parent(parent), locked(false), running(false) { setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::Tool); setPixmap(Pixmap::pixmap(Pixmap::LOCK_OVERLAY)); setAttribute(Qt::WA_TranslucentBackground); showTimer = new QTimer(this); connect(showTimer, &QTimer::timeout, this, &ScreenLockOverlay::fadeOut); showTimer->setSingleShot(true); autoLock = new QTimer(this); connect(autoLock, &QTimer::timeout, this, &ScreenLockOverlay::popup); autoLock->start(SCREEN_LOCK_DELAY); connect(new QShortcut(Qt::Key_L, parent), &QShortcut::activated, this, &ScreenLockOverlay::popup); effect = new QGraphicsOpacityEffect(this); this->setGraphicsEffect(effect); animation = new QPropertyAnimation(effect, "opacity"); animation->setDuration(FADEOUT_TIME); animation->setEasingCurve(QEasingCurve::Linear); hide(); } void ScreenLockOverlay::swipe(bool state) { if (!locked) return; popup(); if (state) { locked = false; autoLock->start(SCREEN_LOCK_DELAY); } } void ScreenLockOverlay::popup() { if (running) return; autoLock->stop(); QPoint start = parent->mapToGlobal(QPoint(0,0)); move(start.x() + parent->width()/2 - width()/2, start.y() + parent->height() - height()); show(); locked = true; showTimer->start(POPUP_DELAY); } void ScreenLockOverlay::fadeIn() { animation->setStartValue(0); animation->setEndValue(1); animation->start(QPropertyAnimation::KeepWhenStopped); } void ScreenLockOverlay::fadeOut() { showTimer->stop(); animation->setStartValue(1); animation->setEndValue(0); running = true; animation->start(QPropertyAnimation::KeepWhenStopped); connect(animation, &QPropertyAnimation::finished, this, [=] () { hide(); running = false; qDebug() << "done" << endl; }); }
-
But - now it looks like it is the calling of show() before the timer starts, that is delayed.
show() is called right away, but it takes a little while before the pixmap is actually shown.
What does that mean? -
But - now it looks like it is the calling of show() before the timer starts, that is delayed.
show() is called right away, but it takes a little while before the pixmap is actually shown.
What does that mean?@Jakob-Clausen said in QPropertyAnimation not responding properly:
show() is called right away, but it takes a little while before the pixmap is actually shown.
Isn't this what you want? the opacity starts at 0 so invisible, it take a while before becoming visible
-
No. It should be shown right away, and stay there a little, and then fade away.