代码之家  ›  专栏  ›  技术社区  ›  Stefan Monov

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

  •  5
  • Stefan Monov  · 技术社区  · 7 年前

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

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

    emitter.someSig.connect(
        function() {
            // do some work here
        },
        Qt.SingleShotConnection
    );
    

    近似重复: Automatically disconnect after first signal emission -但这个问题是关于Python的,我的问题是关于QML和C++的。

    3 回复  |  直到 7 年前
        1
  •  6
  •   derM - not here for BOT dreams    7 年前

    function connectOnce(sig, slot) {
        var f = function() {
            slot.apply(this, arguments)
            sig.disconnect(f)
        }
        sig.connect(f)
    }
    

    作为用法演示:

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

    slot slot2 在单发模式下发送信号 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)
    }
    
        2
  •  6
  •   Stefan Monov    7 年前

    我在这里找到了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;
    });
    

    我仍在等待更好的C++答案,以及QML答案。

        3
  •  0
  •   Michael    3 年前

    QObject::connect 呼叫

    #ifndef  CONNECT_ONCE_H
    # define CONNECT_ONCE_H
    
    # include <QObject>
    # include <memory>
    
    template<typename EMITTER, typename SIGNAL, typename RECEIVER, typename... ARGS>
    void connectOnce(EMITTER* emitter, SIGNAL signal, RECEIVER* receiver, void (RECEIVER::*slot)(ARGS...), Qt::ConnectionType connectionType = Qt::AutoConnection)
    {
      auto connection = std::make_shared<QMetaObject::Connection>();
      auto onTriggered = [connection, receiver, slot](ARGS... arguments){
        (receiver->*slot)(arguments...);
        QObject::disconnect(*connection);
      };
    
      *connection = QObject::connect(emitter, signal, receiver, onTriggered, connectionType);
    }
    
    template<typename EMITTER, typename SIGNAL, typename RECEIVER, typename SLOT, typename... ARGS>
    void connectOnce(EMITTER* emitter, SIGNAL signal, RECEIVER* receiver, SLOT slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
    {
      std::function<void (ARGS...)> callback = slot;
      auto connection = std::make_shared<QMetaObject::Connection>();
      auto onTriggered = [connection, callback](ARGS... arguments) {
        callback(arguments...);
        QObject::disconnect(*connection);
      };
    
      *connection = QObject::connect(emitter, signal, receiver, onTriggered, connectionType);
    }
    
    #endif
    

    关于基于lambda的模板,我无法在直接声明时编译它 std::function 作为一个参数,因为编译器无法将lambda参数识别为有效的候选参数……这就是我声明的原因 SLOT 作为模板参数,然后将其强制转换为 std::函数 .虽然没有我想要的那么简洁,但它很管用。