QML: wait until animations finished

17,644

Solution 1

Here is a working solution based on the answer from #UmNyobe. Well, QML is a declarative language and thus problematic for iterative problems. Any comments are welcomed. Maybe, someone can suggest better (more readable) pure QML program which will produce the same effect.

import QtQuick 1.1
GridView {
    id: mainGrid
    cellWidth: 165; cellHeight: 95
    width: 5*cellWidth; height: 4*cellHeight
    model: myModel
    delegate: myButton
    property string animate: "no"
    property int busy: 0
    signal busyPlus
    signal busyMinus
    onBusyPlus: {
        busy++
    }
    onBusyMinus: {
        busy--
        if (busy == 0) mainGrid.model.algorithm()
    }
    ListModel {
        id: myModel
        property int step: 0
        property int one: 0
        function createModel() {
            for (var i=1; i<=20; i++) {
                append({"display": i})
            }
        }
        function algorithm() {
            if (step == 0) {
                move(0,19,1)
                one = 0
                step++
            }
            else if (step == 1) {
                if (one < 19) {
                    move(one,one+1,1)
                    one++
                    if (one == 19) step++
                }
            }
            else if (step == 2) {
                move(0,1,1)
                move(5,6,1)
                move(10,11,1)
                move(15,16,1)
                step++
            }
        }
        Component.onCompleted: {
            createModel()
            mainGrid.animate = "yes"
            algorithm()
        }
    }
    Component {
        id: myButton
        Item {
            id: item
            width: mainGrid.cellWidth-5; height: mainGrid.cellHeight-5;
            Rectangle {
                id: box
                parent: mainGrid
                x: item.x; y: item.y;
                width: item.width; height: item.height;
                border.width: 1
                Text {
                    anchors.centerIn: parent
                    text: display
                    font.pixelSize: 48
                }
                Behavior on x {
                    enabled: mainGrid.animate == "yes"
                    NumberAnimation {
                        id: animationX;
                        duration: 1000;
                        onRunningChanged: {
                            if (animationX.running) mainGrid.busyPlus()
                            else mainGrid.busyMinus()
                        }
                    }
                }
                Behavior on y {
                    enabled: mainGrid.animate == "yes"
                    NumberAnimation {
                        id: animationY;
                        duration: 1000;
                        onRunningChanged: {
                            if (animationY.running) mainGrid.busyPlus()
                            else mainGrid.busyMinus()
                        }
                    }
                }
            }
        }
    }
}

Solution 2

function doSomething() {
    myModel.move(...)
    while (myModel.busy) {}
    myModel.move(...)
}

I am not good with javascript. But why do you busy loop? I would create 2 functions.

  • The first one make myModel.move(), then prepare the field for a future event (like creating a hidden button which will be clicked)
  • The second one will be called when the the future event is created. With the example above it will be Onclick.

It seems that onRunningChanged is a definition of event handler. Why not creating the same , lets call it onModelIdle like

...
   onRunningChanged: {
        if (animationX.running) {
            console.log("Animation start");
            myModel.busy = myModel.busy + 1
        } else {
            console.log("Animation stop");
            myModel.busy = myModel.busy - 1
            if(myModel.busy == 0)
              // don't know the code, but trigger the onModelIdle event which 
              // be handled below
        }
    }
...

ListModel {
   id: myModel
   ...

   onModelIdle{
       myModel.move(...)
   }
}
Share:
17,644
meolic
Author by

meolic

Chief Technology Officer at Operato

Updated on June 09, 2022

Comments

  • meolic
    meolic about 2 years

    I am trying to make visible animations sequential. I have a javascript function, which call myModel.move() two times. I have a GridView to show myModel and I have "Behavior on x" animation, thus I can visualy see the movements. But, both movement animatons runs in paralell (the small delay between them is not noticeable).

    My idea was to add a counter of how many animations was started and how many of them already finished. Something like this;

    Behavior on x {
        NumberAnimation {
            id: animationX;
            duration: 500;
            onRunningChanged: {
                if (animationX.running) {
                    console.log("Animation start");
                    myModel.busy = myModel.busy + 1
                } else {
                    console.log("Animation stop");
                    myModel.busy = myModel.busy - 1
                }
            }
        }
    }
    

    This works as expected. Then, I add a loop to my javascript function to wait until all the animations finished.

    ListModel {
        id: myModel
        property int busy: 0
        function doSomething() {
            myModel.move(...)
            while (myModel.busy) {}
            myModel.move(...)
        }
    }
    

    Here is the problem. I can see that after the first move() all neccessary animations started, but nothing can be seen and none of the animation finished. I have some kind of a deadlock. How to solve this?