Q: Qt - [c++] PropertyAnimation from WorkerThread
-
Hi everyone!
I am using a QPropertyAnimation from within a QFuture.
Unfortunately, my program dies from time to time, stating the following:QObject::startTimer: Timers can only be used with threads started with QThread QBasicTimer::start: Timers cannot be started from another thread QPropertyAnimation::updateState (): Changing state of an animation without target
How can i make the PropertyAnimation thread-safe?
best regards, kevin
-
@kevin_d said in Q: Qt - [c++] PropertyAnimation from WorkerThread:
Hi everyone!
I am using a QPropertyAnimation from within a QFuture.
Unfortunately, my program dies from time to time, stating the following:QObject::startTimer: Timers can only be used with threads started with QThread QBasicTimer::start: Timers cannot be started from another thread QPropertyAnimation::updateState (): Changing state of an animation without target
How can i make the PropertyAnimation thread-safe?
It's not clear whether the C++ QPropertyAnimation or QML PropertyAnimation is under discussion. Either way, the simple explanation is that neither class can be used from a different thread than the target object.
Reading https://doc.qt.io/qt-6/threads-qobject.html explains why and may reveal some options for this use case. It also helps to explain the first warning: QtConcurrent threads have no event dispatcher, and therefore can't play host to a timer.
Answering the question, make a property animation safe by not using it with threads. Threads are frequently unnecessary and always complicate a program's logic.
-
Hello!
Sorry for the delay and thanks for answering.
I am using a QPropertyAnimation to move the Camera on a Qt3D scene during a sequence, triggered in a QFuture.
- Move the camera upwards (QPropertyAnimation on the Camera)
- Wait for the animation to finish : QThread::msleep(1000)
- Place a figure
- Move the camera back down
- Wait for the Animation to finish : QThread::msleep(1000)
- Release a semaphore to trigger waiting action
futureSwitch = QtConcurrent::run([this, party] { semAnimation->acquire(); QThread::msleep(300); MoveCameraToTop(); QThread::msleep(400); DropFigure(); QThread::msleep(600); MoveCameraToBottom(); QThread::msleep(1800); return 1; });
Animation:
void MoveCameraToBottom() { // class CameraAnimation : public QPropertyAnimation camPosAnimation = new CameraAnimation(camera, CameraAnimation::CamPropertyType::Position); camPosAnimation->setParent(obj); camPosAnimation->startPos = pos_start; camPosAnimation->endPos = position; camPosAnimation->trigger(); }
Why using a future for the animation sequence?
... because when triggered in the main thread, the UI does not update the scene, and animations are not updating live (especially with QThread::msleep()). -
@kevin_d If I understand the description, the goal is:
- an animation that moves the camera up
- an action that occurs at the end of step 1
- an animation that moves the camera down
The correct way to determine when a QPropertyAnimation completes is to use the finished() signal. No sleeping, threading, semaphores, or explicit timers are required.
QPropertyAnimation upAnimator, downAnimator; QObject::connect(&upAnimator, &QAbstractAnimation::finished, &midpoint); QObject::connect(&downAnimator, &QAbstractAnimation::finished, []() { qDebug() << "Done!"; }); void beginAnimation() { upAnimator.start(); } void midpoint() { DropFigure(); downAnimator.start(); }
-
Thank you. This way the application is clearly more stable.
This works also fine, when i wait for all the animations:
QEventLoop loopPos; connect(myAnimation, &QParallelAnimationGroup::finished, &loopPos, &QEventLoop::quit ); loopPos.exec();
... and if needed use a
QParallelAnimationGroup
best regards!
-