1
votes

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?

1
Without knowing more details, attempting to block a property change signal seems kind of like a hack. Can you elaborate on what you are really trying to do? You might have a good reason for this, but there might be a better way... - selbie
The CustomCppItem in my case is a QQuickPaintedItem which calls a library for map drawing. When I select an item from the combobox, I want to tell the MapItem which area should be focused. There are cases where I override the combobox selection where I dont want to change the map focus. In those cases, I am setting ignoreNextChanged to true. If this is already the best way, I wont change it. - feedc0de
How would the flag get cleared with a signal blocker? Currently it gets cleared in your signal handler. - GrecKo
I think I could clear it right after the currentIndex = 42, right? - feedc0de
@DanielBrunner - Thanks for the explanation. Even though this is all in the same QML file, the reason I said this is odd is because the logic for the map to decide how to react to to a combo box change is being handled by the ComboBox item and not by the map/CusomCppItem itself. I would setup a Connection such that CustomCppItem can listen for comboBox.onCurrentIndexChanged itself (or another signal thrown from onCurrentIndexChanged). And then your map can decide how to react. - selbie

1 Answers

2
votes

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.