Running a QML animation for property changes

I am implementing an application with some code intensive processor. This code should update the interface developed in QML to display its results.

The interface is made of a grid and several blocks with some content inside. Grid

fully implemented in C ++, but Block

only QML.

Block.qml

import QtQuick 2.3

Item {
    id: root

    // lot of non-related stuff here.

    Behavior on x {
        NumberAnimation {
            duration: 1000
            easing.type: Easing.Linear

            onRunningChanged: {
                console.log("behavior on x");
            }
        }
    }

    Behavior on y {
        NumberAnimation {
            duration: 1000
            easing.type: Easing.Linear

            onRunningChanged: {
                console.log("behavior on y");
            }
        }
    }
}

      

Each of the blocks is created in C ++ and placed accordingly.

The problem is that several times I have to move the blocks and I would like to trigger the animation. For this, I've included elements Behavior

, as you've probably noticed.

But when I change the position of the block from C ++ code, the behavior is not triggered. Block

just changes its position without animation.

The code I'm using to change the position of the block:

void Grid::setBlockPosition(QQuickItem *block, unsigned i, unsigned j)
{
    float x, y;
    x = GRID_MARGIN_LEFT + j * this->_blockSize;
    y = GRID_MARGIN_TOP + (GRID_ROWS - i - 1) * this->_blockSize;
    block->setPosition(QPointF(x, y));
}

      

I tried to change x

as well, then y

instead of changing both at the same time, but it gave the same result.

How can I invoke behavior in x

and y

from properties in C ++?

+3


source to share


1 answer


That's a good question. I took a look at the source QQuickItem

and setX () , setY () and setPosition () have very similar code codes, with all of them emitting geometryChanged()

. You might think that this would be enough, but it turns out that it is not :

You should always use QObject :: setProperty (), QQmlProperty, or QMetaProperty :: write () to change the value of a QML property to ensure that the QML engine knows when the property has changed. For example, let's say you have a custom type PushButton with a buttonText property that internally reflects the value of the m_buttonText member variable. Changing a member variable directly [...] is not a good idea [...].

Since the value is changed directly, this bypasses Qt's metaobject system and the QML engine is not aware of the property change. This means that the property bindings to the buttonText will not be updated and no onButtonTextChanged handlers will be called.

Using an example specific to your code: if you change the x property of your block in QML, for example, the metaobject system knows about this change, but Behavior

gets notified and sets the property directly, in increments until it reaches its goal. When you set the property yourself, it Behavior

doesn't know that something has changed and therefore doesn't do anything.



So you have to call setProperty("x", x)

and setProperty("y", y)

:

#include <QApplication>
#include <QtQuick>
#include <QtQml>

class Grid : public QQuickItem
{
    Q_OBJECT
public:
    Grid(QQuickItem *parent) {
        setParentItem(parent);
    }

public slots:
    void addBlocks() {
        QQmlComponent *blockComponent = new QQmlComponent(qmlEngine(parentItem()), "Block.qml");
        for (int i = 0; i < 4; ++i) {
            QQuickItem *block = qobject_cast<QQuickItem*>(blockComponent->create());
            Q_ASSERT(!blockComponent->isError());
            block->setParentItem(this);
            block->setProperty("x", i * 100);
            block->setProperty("y", i * 100);
            mBlocks.append(block);
        }
    }

private:
    QList<QQuickItem*> mBlocks;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    QQuickItem *contentItem = engine.rootObjects().first()->property("contentItem").value<QQuickItem*>();
    Grid *grid = new Grid(contentItem);
    grid->addBlocks();

    return a.exec();
}

#include "main.moc"

      

I have added this information to the detailed documentation QQuickItem

; hope others see this if they run into this problem.

+4


source







All Articles