如何在 QML 中创建延迟函数?

How to create delay function in QML?

我想在 javascript 中创建一个延迟函数,它采用延迟时间参数,这样我就可以使用它在我的 JavaScript 行的执行之间引入延迟QML 应用程序。它可能看起来像这样:

function delay(delayTime) {
  // code to create delay
}

我需要函数体 delay()。请注意,JavaScript 的 setTimeout() 在 QML 中不起作用。

正如您问题的评论中所建议的那样,Timer 组件是一个很好的解决方案。

function Timer() {
    return Qt.createQmlObject("import QtQuick 2.0; Timer {}", root);
}

timer = new Timer();
timer.interval = 1000;
timer.repeat = true;
timer.triggered.connect(function () {
    print("I'm triggered once every second");
})

timer.start();

以上是我目前的使用方式,下面是我可能如何实现您问题中的示例。

function delay(delayTime) {
    timer = new Timer();
    timer.interval = delayTime;
    timer.repeat = false;
    timer.start();
}

(什么都不做;继续阅读)

虽然您正在寻找它的确切实现方式表明您正在寻找它以 阻止 直到程序的下一行执行。但这不是解决此问题的好方法,因为它还会阻止程序中的 其他所有内容 ,因为 JavaScript 仅在单个执行线程中运行。

另一种方法是传递回调。

function delay(delayTime, cb) {
    timer = new Timer();
    timer.interval = delayTime;
    timer.repeat = false;
    timer.triggered.connect(cb);
    timer.start();
}

这将允许您这样使用它。

delay(1000, function() {
    print("I am called one second after I was started.");
});

希望对您有所帮助!

编辑:以上内容假设您在一个单独的 JavaScript 文件中工作,稍后您将其导入到您的 QML 文件中。要直接在 QML 文件中执行等效操作,您可以这样做。

import QtQuick 2.0

Rectangle {
    width: 800
    height: 600

    color: "brown"

    Timer {
        id: timer
    }

    function delay(delayTime, cb) {
        timer.interval = delayTime;
        timer.repeat = false;
        timer.triggered.connect(cb);
        timer.start();
    }

    Rectangle {
        id: rectangle
        color: "yellow"
        anchors.fill: parent
        anchors.margins: 100
        opacity: 0

        Behavior on opacity {
            NumberAnimation {
                duration: 500
            }
        }
    }

    Component.onCompleted: {
        print("I'm printed right away..")
        delay(1000, function() {
            print("And I'm printed after 1 second!")
            rectangle.opacity = 1
        })
    }
}

不过,我不相信这是解决您实际问题的方法;要延迟动画,您可以使用 PauseAnimation.

你可以使用 QtTest

import QtTest 1.0
import QtQuick 2.9

ApplicationWindow{
    id: window

    TestEvent {
        id: test
    }

    function delay_ms(delay_time) {
        test.mouseClick(window, 0, 0, Qt.NoButton, Qt.NoModifier, delay_time)
    }
}

Marcus 的回答可以解决问题,但是有一个大问题

问题是回调即使在触发一次后仍保持连接到 triggered 信号。这意味着如果您再次使用该延迟功能,计时器将再次触发之前连接的 all 回调。所以你应该在触发后断开回调。

这是我的延时功能增强版:

Timer {
    id: timer
    function setTimeout(cb, delayTime) {
        timer.interval = delayTime;
        timer.repeat = false;
        timer.triggered.connect(cb);
        timer.triggered.connect(function release () {
            timer.triggered.disconnect(cb); // This is important
            timer.triggered.disconnect(release); // This is important as well
        });
        timer.start();
    }
}

...

timer.setTimeout(function(){ console.log("triggered"); }, 1000);

这是另一个变体,它利用 Component 对象来容纳 Timer 对象。

然后我们实现一个setTimeoutlook-a-like函数来动态创建和调用这个Timer对象。

N.B。答案假定 Qt5.12.x 包含 ECMAScript 7(因此包含 ECMAScript 6)以利用参数快捷方式、剩余参数和传播语法:

    function setTimeout(func, interval, ...params) {
        return setTimeoutComponent.createObject(app, { func, interval, params} );
    }

    function clearTimeout(timerObj) {
        timerObj.stop();
        timerObj.destroy();
    }

    Component {
        id: setTimeoutComponent
        Timer {
            property var func
            property var params
            running: true
            repeat: false
            onTriggered: {
                func(...params);
                destroy();
            }
        }
    }

在下面的代码片段中,我们将在从现在开始的 0-1000 毫秒之间的随机时间延迟中调用 console.log(31)console.log(32)console.log(33)

console.log("Started");
setTimeout(console.log, Math.floor(1000 * Math.random()), 31);
setTimeout(console.log, Math.floor(1000 * Math.random()), 32);
setTimeout(console.log, Math.floor(1000 * Math.random()), 33);

另请参阅:https://community.esri.com/groups/appstudio/blog/2019/05/22/ecmascript-7-settimeout-and-arrow-functions

Bumsik Kim 的答案很好,这个答案稍微改变了它,以便可以重复使用计时器,然后在需要时停止并重新使用。

在需要时添加的计时器 QML。

// Allow outside access (optional)
property alias timer: timer

Timer {
    id: timer

    // Start the timer and execute the provided callback on every X milliseconds
    function startTimer(callback, milliseconds) {
        timer.interval = milliseconds;
        timer.repeat = true;
        timer.triggered.connect(callback);
        timer.start();
    }

    // Stop the timer and unregister the callback
    function stopTimer(callback) {
        timer.stop();
        timer.triggered.disconnect(callback);
    }
}

可以按如下方式使用。

timer.startTimer(Foo, 1000); // Run Foo every 1 second
timer.stopTimer(Foo); // Stop running Foo

timer.startTimer(Bar, 2000); // Run Bar every 2 seconds
timer.stopTimer(Bar); // Stop running Bar

function Foo() {
    console.log('Executed Foo');
}

function Bar() {
    console.log('Executed Bar');
}

这是我对之前答案的持续改进 and

将此文件/组件添加到您的项目中:

Scheduler.qml

import QtQuick 2.0

Timer {
    id: timer

    // Execute the callback asynchronously
    function async(callback)
    { _start(callback, 0, false); }

    // Start the timer and execute the provided callback ONCE after X milliseconds
    function delay(callback, milliseconds)
    { _start(callback, milliseconds, false); }

    // Start the timer and execute the provided callback on every X milliseconds
    function periodic(callback, milliseconds)
    { _start(callback, milliseconds, true); }

    function _start(callback, milliseconds, isRepeat) {
        timer.interval = milliseconds;
        timer.repeat = isRepeat;
        timer.triggered.connect(callback);
        timer.start();
    }

    // Stop the timer and unregister the callback
    function cancel(callback) {
        timer.stop();
        timer.triggered.disconnect(callback);
    }
}

然后,在另一个组件中实现,如:

...
Scheduler { id: scheduler; }
scheduler.delay( function(){ console.log('Delayed'); }, 3000 );

您可以使用此处显示的匿名函数,或者回调到命名函数,这使得以后能够使用 cancel 方法。请注意,async 将尽快启动(当应用程序有时间时),而不会阻止您的调用代码块的执行。

如果您希望实例化多个定时器,定时器将根据“调度程序”独立分组/管理。