触发特性更改的QML动画

Triggering QML animation for property changes

本文关键字:QML 动画      更新时间:2023-10-16

我正在用一些CPU密集型代码实现一个应用程序。此代码必须更新QML中设计的接口以显示其结果。

该界面由一个网格和几个块组成,其中包含一些内容。Grid是完全C++实现的,而Block只是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");
            }
        }
    }
}

每个块都是在C++中创建的,并相应地进行定位。

问题是,有时我必须移动块,我想触发一个动画。为了实现这一点,我包含了Behavior元素,您可能已经注意到了。

但是,当我从C++代码更改块的位置时,行为不会被触发。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));
}

我也尝试过更改xy,而不是同时更改两者,但结果相同。

如何从C++触发xy属性上的行为?

这是个好问题。我研究了QQuickItem的源代码,setX()、setY()和setPosition()都有非常相似的代码路径,它们都发出geometryChanged()。你可能会认为这就足够了,但事实证明这还不够:

您应该始终使用QObject::setProperty()、QQmlProperty或QMetaProperty::write()来更改QML属性值,以确保QML引擎了解属性更改。例如,假设您有一个自定义类型PushButton,其buttonText属性在内部反映m_buttonText成员变量的值。直接修改成员变量[…]不是一个好主意[…]。

由于该值是直接更改的,因此这会绕过Qt的元对象系统,并且QML引擎不会意识到属性更改。这意味着到buttonText的属性绑定将不会更新,也不会调用任何onButtonTextChanged处理程序。

使用特定于代码的示例:例如,如果您在QML中更改块的x属性,则元对象系统会意识到这一更改,Behavior会收到通知,并以增量直接设置该属性,直到它达到目标。当您自己直接设置属性时,Behavior不知道有任何更改,因此它什么也不做。

因此,您应该调用setProperty("x", x)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"

我已经将这些信息添加到QQuickItem文档的详细描述中;如果遇到这个问题,希望其他人也能看到。