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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28507619/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:54:17  来源:igfitidea点击:

How to create delay function in QML?

javascriptqmldelay

提问by Sна?ош?а?

I would like to create a delay function in javascript that takes a parameter of amount of time to delay, so that I could use it do introduce delay between execution of javascript lines in my QML application. It would perhaps look like this:

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

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

I need the body of the function delay(). Note that setTimeout()of javascript doesn't work in QML.

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

回答by Marcus Ottosson

As suggested in the comments to your question, the Timercomponent is a good solution to this.

正如对您的问题的评论所建议的那样,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();

The above would be how I'm currently using it, and here's how I might have implemented the example in your question.

以上将是我目前使用它的方式,这是我如何在您的问题中实现示例。

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

(Which doesn't do anything; read on)

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

Though the exact way you are looking for it to be implemented suggests that you are looking for it to blockuntil the next line of your program executes. But this isn't a very good way to go about it as it would also block everything elsein your program as JavaScript only runs in a single thread of execution.

尽管您正在寻找实现它的确切方式表明您正在寻找它来阻止直到您的程序的下一行执行。但这不是一个很好的方法,因为它还会阻塞程序中的所有其他内容,因为 JavaScript 仅在单个执行线程中运行。

An alternative is to pass a callback.

另一种方法是传递回调。

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

Which would allow you to use it as such.

这将允许您像这样使用它。

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

Hope it helps!

希望能帮助到你!

Edit: The above assumes you're working in a separate JavaScript file that you later import into your QML file. To do the equivalent in a QML file directly, you can do this.

编辑:以上假设您在一个单独的 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
        })
    }
}

I'm not convinced that this is the solution to your actual problem however; to delay an animation, you could use PauseAnimation.

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

回答by Bumsik Kim

Marcus' answer does the job, but there is one big problem.

马库斯的回答可以解决问题,但有一个大问题

The problem is that the callback keeps connected to triggeredsignal even after triggered once. This means that if you use that delay function again, the timer will triggers allcallbacks connected before again. So you should disconnect the callback after triggered.

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

This is my enhanced version of the delay function:

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

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);

回答by Stephen Quan

Here's another variation which utilizes the Componentobject to house the Timerobject.

这是另一种利用Component对象来容纳Timer对象的变体。

Then we implement a setTimeoutlook-a-like function to dynamically create and invoke this Timerobject.

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

N.B. The answer assumes Qt5.12.x which includes ECMAScript 7 (and therefore ECMAScript 6) to utilize parameter shortcuts, rest parameters and spread syntax:

NB 答案假设 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()
            }
        }
    }

In the following snippet, we will invoke console.log(31, 32, 33)in a random time delay between 0-1000ms from now.

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

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

See also: https://community.esri.com/groups/appstudio/blog/2019/05/22/ecmascript-7-settimeout-and-arrow-functions

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

回答by foochow

The answer from Bumsik Kim is great, this answer changes it slightly so that the timer can be used on a repeating basis and then stopped and reused when desired.

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

The QML for the timer to add where required.

在需要的地方添加计时器的 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);
    }
}

This can be used as follows.

这可以如下使用。

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');
}

回答by fcying

you can use QtTest

你可以使用 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)
    }
}