[Qt-qml] Pause animation until a condition is met

Adriano Rezende adriano.rezende at openbossa.org
Mon Sep 13 18:10:15 CEST 2010


On Fri, Sep 10, 2010 at 3:32 PM, Alex <alexlordax+qt at gmail.com> wrote:
> (1) There is a scoping issue - onConditionChanged continues to be
> invoked long after the associated ScriptAction is stopped.  This may
> produce undesirable effects in those cases where there are multiple
> guards.  For example, in the case where there are two such guards, and
> the second one is "executing," the onConditionChanged of the first
> guard will get invoked if its condition is met, and resume the
> animation prematurely.  A variation on this problem is that an
> explicitly paused animation may get implicitly resumed when a guard
> condition is met.
>
> (2) It looks like pausing, stopping, and resuming are not synchronous
> operations.  In the example above, you will see that the guards are
> adjacent.  They both attempt to pause the parent animation, but the
> parent animation manages to run to completion before it is paused.

This is a surprise :). I thought that pause was synchronous, but
looking at the animation code, it just triggers a timer, and waits the
last tick to be completed before it really pauses. This behavior
probably invalidates any attempt to create this kind of component in a
clean way, in the QML side.

The first issue can be easily fixed adjusting onConditionChanged to
check if target is running and if the action has already being
consumed or not. But I wouldn't suggest this component anymore, since
a workaround is needed to compensate the time interval, like adding a
PauseAnimation, and this could also result in race conditions.

> Here is the same test using my polling approach:
>
> GuardAnimation.qml
> ==================
>
> SequentialAnimation {
>    id: guardAnimation
>    property string name
>    property variant target      // Not used
>    property bool condition
>    property bool firstIteration : true
>
>    loops: condition ? 0 : Animation.Infinite
>
>    ScriptAction {
>        script: {
>            if(firstIteration) {
>                console.log(name + ": pausing")
>                firstIteration = false
>            }
>        }
>    }
>    ScriptAction {
>        script: {
>            if(condition) {
>                console.log(name + ": resuming")
>                guardAnimation.running = false
>            }
>        }
>    }
>    PauseAnimation { duration: 200 }
> }

Changing the loop count while the animation is running will mess up
with the AnimationGroup logic and result in bizarre behaviors like
rewinds. Also, a child animation should not be stopped
programmatically, this is the AnimationGroup's job.

If you run your code with the following file:

Item {
    id: root
    width: 200
    height: 200
    property int counter : 0

    Timer {
        repeat: true
        running: true
        interval: 1000
        onTriggered: { counter = (counter + 1) % 2; }
    }

    SequentialAnimation {
        id: animation
        running: true
        ScriptAction { script: console.log("action 1"); }
        GuardAnimation { name: "action2"; target: animation;
condition: root.counter == 0; }
        ScriptAction { script: console.log("action 2"); }
        GuardAnimation { name: "action3"; target: animation;
condition: root.counter == 1; }
        ScriptAction { script: console.log("action 3"); }
        GuardAnimation { name: "action4"; target: animation;
condition: root.counter == 1; }
        ScriptAction { script: console.log("action 4"); }
    }
}

You will see the following output:

action 1
action 2
action 2
action 2
action 3
action 2
action 2
action 3
action 4

This happens due the fact that an AnimationGroup is constantly looking
for the current animation based on the current time. It search all
child animations in sequence, incrementing the duration to see where
it will fit. But when it finds an animation that have an infinite
duration it returns this animation as the current one, even if it's
already being processed.

Maybe the best approach for you, even if it's not so cool, is to
handle these conditions externally, triggering different animations.
You can create a component like a ChainAnimation that have a condition
and a next animation target.

BTW, I've found a QML animation bug while testing these cases. I've
added it in JIRA with a patch:
http://bugreports.qt.nokia.com/browse/QTBUG-13598

> PS: As an aside, just like I noted above about stop/pause/resume, it
> seems that a property value changes are not synchronous.  If I have
> two adjacent ScriptAction blocks within an animation, with the first
> one setting a property to a particular value and the second verifying
> that the value has been set, the verification sometimes fails.  If I
> insert a PauseAnimation between the two ScriptActions, the
> verification succeeds.  I have been running into this with C++-derived
> components, so perhaps this is not an issue in basic property types of
> native QML components?

Do you have a QML code to simulate this problem?

AFAIK properties are set synchronously. Maybe what you are facing is
something related to this specific component.

Br,
Adriano



More information about the Qt-qml mailing list