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

Posted

技术标签:

【中文标题】如何在 QML 中创建延迟函数?【英文标题】:How to create delay function in QML? 【发布时间】:2015-02-13 19:49:54 【问题描述】:

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

function delay(delayTime) 
  // code to create delay

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

【问题讨论】:

只需使用 Timer 元素:doc.qt.io/qt-5/qtimer.html 选项在这里,在 cmets 中。除此之外,动画框架相当丰富,提供了多种控制动画的方式。深入研究文档或发布(编辑)问题以满足您的动画需求。 为什么投反对票?我认为这是一个完全合理的问题。 @MarcusOttosson 感谢您支持我的问题! 【参考方案1】:

正如 cmets 对您的问题所建议的那样,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。

【讨论】:

A 也认为Timer 是不错的解决方案 如果您多次使用它,请小心。请参阅下面 Bumsik Kim 的后续回答。我花了很长时间才意识到我有多个功能仍然连接到我的计时器而没有意识到。 我觉得你需要调用 timer.destroy(); ``` ... timer.triggered.connect(cb); timer.triggered.connect(function () timer.destroy(); ); ... ``` 延迟正在冻结 qml 2.15 应用程序。 我刚开始使用 JavaScript,因为还有一些 QML 工作要做。您不应该使用timer = Timer(); 代替timer = new Timer(); 吗? new 将创建一个新对象并将其作为this 注入到Timer,但您不使用它。所以这里的new 关键字对我来说似乎是多余的和令人困惑的。【参考方案2】:

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

【讨论】:

我不再做 QML,但你说的似乎是对的,所以 +1。 其实你不应该也断开这个匿名函数吗? timer.triggered.connect(function() ... ); @manicaesar 对。我修好了它。感谢您提及它!【参考方案3】:

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

然后我们实现一个setTimeout 类似函数来动态创建和调用这个Timer 对象。

注意答案假设 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();
            
        
    

在接下来的 sn-p 中,我们将从现在开始以 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

【讨论】:

封装得很好,除了 setTimeoutComponent.createObject(app, …) 中对 app 的引用。在这里传递null 是安全的(see)。不管是垃圾回收,对象都是self-destroy()ing。【参考方案4】:

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

【讨论】:

【参考方案5】:

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

【讨论】:

其实不错的计时器,想知道为什么它没有得到更多的支持。【参考方案6】:

这是我对先前答案 https://***.com/a/62051450/3220983 和 https://***.com/a/50224584/3220983.. 的持续改进。

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

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 将尽快启动(当应用有时间时),而不会阻止调用代码块的执行。

如果您希望实例化其中的多个,计时器将按“调度程序”独立分组/管理。

【讨论】:

以上是关于如何在 QML 中创建延迟函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 QML 中创建自定义对象?

如何在 Qt QML 中创建多屏应用程序

如何在 QML 中创建 PyQT 注册类型的动态实例

如何在 QML 中创建动态委托?

如何在 QT QML 中创建自己的目录?

如何在 python 中创建路径并将其显示在 qml 画布上