将信号连接到插槽,但只调用一次插槽,然后自动断开它们的连接

Ste*_*nov 4 qt signals-slots qml observer-pattern qtquick2

考虑以下JS代码:

function handleSig() {
    emitter.someSig.disconnect(handleSig);
    // do some work here
}

emitter.someSig.connect(handleSig);
Run Code Online (Sandbox Code Playgroud)

是否可以在没有显式断开连接和命名函数的情况下进行编写?

理想情况下,我想要这样的东西:

emitter.someSig.connect(
    function() {
        // do some work here
    },
    Qt.SingleShotConnection
);
Run Code Online (Sandbox Code Playgroud)

几乎重复:第一次发出信号后自动断开连接 -但是这个问题与Python有关,而我的问题与QML和C ++有关。

Ste*_*nov 5

我在这里找到了C ++的答案,尽管有点不雅致:

https://forum.qt.io/topic/67272/how-to-create-a-single-shot-one-time-connection-to-a-lambda/2

该链接中的代码:

QMetaObject::Connection * const connection = new QMetaObject::Connection;
*connection = connect(_textFadeOutAnimation, &QPropertyAnimation::finished, [this, text, connection](){
    QObject::disconnect(*connection);
    delete connection;
});
Run Code Online (Sandbox Code Playgroud)

我仍在寻求更好的C ++答案和QML答案。

  • 您可以将`shared_ptr`用于连接,则不必`delete`,因为生命周期是在capture子句中管理的 (2认同)

der*_*erM 5

您可以创建一个小的辅助函数,该函数可以为您断开连接,例如:

function connectOnce(sig, slot) {
    var f = function() {
        slot.apply(this, arguments)
        sig.disconnect(f)
    }
    sig.connect(f)
}
Run Code Online (Sandbox Code Playgroud)

作为用法的演示:

import QtQuick 2.7
import QtQuick.Controls 2.0

ApplicationWindow {
    id: myWindow
    visible: true
    width: 600
    height: 600
    color: 'white'

    signal action(string name)

    function slot(name) {
        console.log(name)
    }

    Button {
        text: 'connect'
        onClicked: {
            connectOnce(action, slot)
        }
    }

    Button {
        y: 80
        text: 'action'
        onClicked: {
            action('test')
        }
    }

    function connectOnce(sig, slot) {
        var f = function() {
            slot.apply(this, arguments)
            sig.disconnect(f)
        }
        sig.connect(f)
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的两个按钮即可连接slot,并slot2在单次模式的信号action。Button 动作将触发信号action,该信号将执行与连接的插槽相同的次数。然后它们将立即断开连接。

您可以将函数connectOnce放入库中,以在任何需要的地方使用。


此解决方案很容易扩展为更通用的形式,该形式n通过在闭包中引入一个计数器来连接要执行的函数的时间:

function connectN(sig, slot, n) {
    if (n <= 0) return
    var f = function() {
        slot.apply(this, arguments)
        n--
        if (n <= 0) sig.disconnect(f)
    }
    sig.connect(f)
}
Run Code Online (Sandbox Code Playgroud)