Andreas P
Andreas P

Reputation: 53

How to implement callback-function in QML?

I want to use callback functionality in QML (using Qt5.5.1). When a sensor value changes, I want to call a function of the button, that was pressed at last. I am using standard QML objects without creating custom QML objects with cpp classes. I even got it to run, but I don't understand why it is working like this.

I expect that I need to call the levelChanged_ButtonCallback manually, but that results to TypeError: Property 'levelChanged_ButtonCallback' of object QQuickItem_QML_22(0xd6ab18) is not a function.

The function binded in onPressed is called whenever the level changes, which is good. It's also called whenever the button is pressed, which is not as planned, but doesn't really matter. My main concern is, that I don't really understand where/how the function is called, and I am not sure if there may occur unwanted behaviour.

(I added a timer to simulate sensor values. The two MouseAreas only differ in their color and btnId. In my actual app, I of course use a .qml-file for those buttons.)

Is this "clean code" or may it cause problems? Is there a clean way to do it with QML only or do I need to create custom QML objects via cpp classes? I don't see a way to do this with signals/slots, because depending which button was pressed at last, a different slot should be connected.

Item {
    id: sensor
    property int level: 0
    property int levelTreshold: 3
    property var levelChanged_ButtonCallback

    Timer {
        running: true
        interval: 5000
        repeat: true
        onTriggered: {
            if (sensor.level == 0) {
                sensor.level = sensor.levelTreshold;
            }
            else {
                sensor.level = 0;
            }
            console.log("sensor changed to " + sensor.level);

            /* Not needed, but why? Causes error.*/
            //sensor.levelChanged_ButtonCallback();
        }
    }
}

MouseArea {
    width: 50
    height: 50
    property string btnId: "red"
    onPressed: {
        sensor.levelChanged_ButtonCallback = Qt.binding(function() {
            if (sensor.level >= sensor.levelTreshold) {
                console.log("Button pressed with force: " + btnId);
            }
            else {
                console.log("Button pressed no force: " + btnId);
            }
        });
    }
    onReleased: {
        sensor.levelChanged_ButtonCallback = Qt.binding(function() {
            console.log("No button pressed, do nothing on level change! Release by: " + btnId);
        });
    }

    Rectangle {
        anchors.fill: parent
        color: "red"
    }
}

MouseArea {
    x: 80
    width: 50
    height: 50
    property string btnId: "blue"
    onPressed: {
        sensor.levelChanged_ButtonCallback = Qt.binding(function() {
            if (sensor.level >= sensor.levelTreshold) {
                console.log("Button pressed with force: " + btnId);
            }
            else {
                console.log("Button pressed no force: " + btnId);
            }
        });
    }
    onReleased: {
        sensor.levelChanged_ButtonCallback = Qt.binding(function() {
            console.log("No button pressed, do nothing on level change! Release by: " + btnId);
        });
    }
    Rectangle {
        anchors.fill: parent
        color: "blue"
    }
}

Upvotes: 2

Views: 4102

Answers (1)

t4ggno
t4ggno

Reputation: 130

If you change a value in QML you can detect that with an onPropertyChanged slot / function. That's how Qt works (Signals and Slots). No need to call that on your own.

One way to do it:

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Item {
        id: sensor
        property var lastActiveButton: undefined
        property int level: 0
        property int levelTreshold: 3
        property var levelChanged_ButtonCallback

        onLevelChanged:{
            if(lastActiveButton){
                var lastButtonString = lastActiveButton === mouseRed ? "red" : "blue"
                if(level >= levelTreshold) console.log("Button pressed with force: " + lastButtonString);
                else console.log("Button pressed no force: " + lastButtonString);
            }
            else{
                console.log("No button active...");
            }
        }

        Timer {
            running: true
            interval: 5000
            repeat: true
            onTriggered: {
                if (sensor.level == 0)  sensor.level = sensor.levelTreshold;
                else  sensor.level = 0;
                console.log("sensor changed to " + sensor.level);
            }
        }
    }

    MouseArea {
        id: mouseRed
        width: 50
        height: 50
        onPressed: sensor.lastActiveButton = this
        onReleased: sensor.lastActiveButton = undefined

        Rectangle {
            anchors.fill: parent
            color: "red"
        }
    }

    MouseArea {
        id: mouseBlue
        x: 80
        width: 50
        height: 50
        onPressed: sensor.lastActiveButton = this
        onReleased: sensor.lastActiveButton = undefined

        Rectangle {
            anchors.fill: parent
            color: "blue"
        }
    }
}

Upvotes: 3

Related Questions