feedc0de
feedc0de

Reputation: 3796

QSignalBlocker in Qml/QtQuick2?

I want to set the currentIndex of a ComboBox dynamically without triggering my attached signal.

I am currently using a bool property to set a flag that gets cleared when my signal gets called.

Item {
    property bool ignoreNextChanged: false

    CustomCppItem  {
        id: customItem

        onSomethingHappened: {
            ignoreNextChanged = true
            comboBox.currentIndex = 42
        }
    }

    ComboBox {
        id: comboBox

        currentIndex: -1
        onCurrentIndexChanged: {
            if(ignoreNextChanged) {
                ignoreNextChanged = false
                return
            }

            // Removed code here
        }
    }
}

Coming from Qt/C++, I would like to use some sort of QSignalBlocker here instead of my own property.

Is there some equivalent in Qml/QtQuick2?

Upvotes: 1

Views: 851

Answers (1)

GrecKo
GrecKo

Reputation: 7160

For this I would implement an attached type in c++ :

signalblockerattachedtype.h :

#ifndef SIGNALBLOCKERATTACHEDTYPE_H
#define SIGNALBLOCKERATTACHEDTYPE_H

#include <QObject>
#include <qqml.h>

class SignalBlockerAttachedType : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)

public:
    explicit SignalBlockerAttachedType(QObject *object = nullptr) : QObject(object), m_object(object)
    {
    }

    ~SignalBlockerAttachedType() {
        if (m_object)
            m_object->blockSignals(false);
    }

    bool enabled() const
    {
        return m_enabled;
    }
    void setEnabled(bool enabled)
    {
        if (m_enabled == enabled)
            return;

        if (m_object)
            m_object->blockSignals(enabled);

        m_enabled = enabled;
        emit enabledChanged();
    }

    static SignalBlockerAttachedType *qmlAttachedProperties(QObject *object) {
        return new SignalBlockerAttachedType(object);
    }

signals:
    void enabledChanged();

private:
    bool m_enabled = false;
    QObject *m_object;
};

QML_DECLARE_TYPEINFO(SignalBlockerAttachedType, QML_HAS_ATTACHED_PROPERTIES)

#endif // SIGNALBLOCKERATTACHEDTYPE_H

main.cpp :

#include "signalblockerattachedtype.h"
// ...
qmlRegisterUncreatableType<SignalBlockerAttachedType>("fr.grecko.SignalBlocker", 1, 0, "SignalBlocker", "SignalBlocker is only available as an attached type.");
// ...
engine.load(QUrl(QLatin1String("qrc:/usage.qml")));

usage.qml :

import fr.grecko.SignalBlocker 1.0
// ...

Item {
    id: rootItem
    property alias currentIndex: comboBox.currentIndex
    onCurrentIndexChanged: {
        // this won't get called from a change in onSomethingHappened
    }
    CustomCppItem  {
        id: customItem

        onSomethingHappened: {
            rootItem.SignalBlocker.enabled = true;
            rootItem.currentIndex = 42
            rootItem.SignalBlocker.enabled = false;
        }
    }

    ComboBox {
        id: comboBox
        currentIndex: -1
    }
}

Even though the SignalBlocker works on the ComboBox, you don't want to put it on the ComboBox. Doing so will prevent it to update its display when the index changes.

In your situation, I don't think this solution is better than using a simple flag.

Upvotes: 2

Related Questions