HOWTO dynamically change animation limits when using States and Transitions



  • I would like to accomplish the following:

    • animate a displayed number from 0 to a random value

    • animate it back down to 0

    • briefly display a "—", then go back to start

    In the code below, I have three States corresponding to the above cases:

    1. "idle": displays the "—".
    2. "on": displays the random number. Transition from "idle" to "on" should animate the display going up from 0.
    3. "off" displays 0. Transition from "on" to "off" should animate the display going down to 0. "off" to "idle" changes it back to a "—".
    import QtQuick 2.0
    
    Rectangle {
        id: root
        width: 400
        height: 200
        color: "LightGoldenRodYellow"
    
        Text {
            id: myText
            property int testVal: 0
            property int maxVal  // value to animate to?
            text: testVal.toString()
            anchors.centerIn: parent
            font.pointSize: 40
        }
    
        state: "idle"
    
        states: [
            State {
                name: "on"
                StateChangeScript { script: console.log("Entered on. maxVal = ") + myText.maxVal }
                PropertyChanges { target: myText; testVal: Math.floor(Math.random()*100) }
            },
            State {
                name: "off"
                StateChangeScript { script: console.log("Entered off") }
                PropertyChanges { target: myText; testVal: 0.0 }
                // Reset to default props: testVal = 0
            },
            State {
                name: "idle"
                StateChangeScript { script: console.log("Entered idle") }
                // Override myText from "0" to mdash. BUT this needs to be a temporary
                // assignment in this state; everywhere else we want to preserve the testVal binding.
                PropertyChanges { target: myText; explicit: true; text: "—" }
                PropertyChanges { target: myText; maxVal: Math.floor(Math.random()*100) }
            }
        ]
    
        transitions: [
            Transition {
                from: "idle"; to: "on"
                SequentialAnimation {
                    // text was emdash, change to 0
                    //PropertyAction { target: myText; property: "text"; value: myText.testVal.toString() }
                    //ScriptAction { script: console.log("Going idle -> on. maxVal = ") + myText.maxVal }
                    NumberAnimation {
                        //target: myText; property: "testVal"; to: myText.maxVal; duration: 1000;
                        target: myText; property: "testVal"; duration: 1000;
                        easing.type: Easing.InOutCubic
                    }
                    PauseAnimation { duration: 1000 }
                    ScriptAction { script: root.state = "off" }
                }
            },
    
            Transition {
                from: "on"; to: "off"
                SequentialAnimation {
                    NumberAnimation {
                        target: myText; property: "testVal"; duration: 1000;
                        easing.type: Easing.InOutCubic
                    }
    
                    PauseAnimation { duration: 100 }
                    ScriptAction { script: root.state = "idle"; }
                }
            },
    
            Transition {
                from: "off"; to: "idle"
                SequentialAnimation {
                    PauseAnimation { duration: 1000 }
                    ScriptAction { script: root.state = "on"; }
                }
            }
    
        ]
    
        Component.onCompleted: { console.log("Starting FSM"); root.state = "on" }
    
    }
    

    Based on my understanding of States and Transitions, the explicit: true in "idle" ought to temporarily assign the Text string to "—". Then I want to transition to "on". However, the animation limit in this transition needs to be set dynamically via Math.random(). I set this in the PropertyChanges object corresponding to the "on" state, but this doesn't work. What I see happening is:

    1. The "—" is displayed out of idle.
    2. The dash continues to be displayed in "on".
    3. The transition from "on" to "off" gets animated correctly from a random "on" value.
    4. The transition from "off" to "idle" also functions correctly.

    Why does the idle --> on transition not work? testVal is 0 in idle. text has been temporarily set to "—" but as soon as we transition out of idle, the defined binding of text: testVal.toString() should get restored. It is possible that the animation limit is not correctly calculated (because the value becomes known only in "on", but even so, I should have at least seen the text change from the dash to 0 at the exit out of idle.

    I have tried setting a separate maxVal property on the Text object and using that as the limit of the animation in the transition from "idle" to "on" (that value is calculated before the transition begins, so it should have avoided the problem noted above), but that didn't work either.

    Also tried to advance root.state as part of the StateChangeScript in each state, but get the error Can't apply a state change as part of a state definition.

    How can I get this to work?



  • I am able to work around this by animating the change to testVal inside a Behavior; the display now functions as desired. But I'm still puzzled as to why the animation within the Transition doesn't work. Any insights are much appreciated.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.