F-Kae
F-Kae

Reputation: 155

QML condition logic for updating a property

I am searching for a logic, so that an Image source property is changed depending on some different conditions.

I have created a small example with three conditions and three requirements build by those conditions.

If a requirement is active I want to see its picture in the Image area. If a requirement is not active I do not want to see its image in the image area. If two requirements would be active at the same time, it is ok to see just one of them.

I have problems with the code, because in case one requirement gets inactive and another gets active at the same time, the last requirement will control the content. But I want to have a higher priority on showing an image than not showing an image.

Is there any frequent way how to solve this?

I have the following code:

main.qml:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0

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

    property bool condition1: false
    property bool condition2: false
    property bool condition3: false

    Image{
        id: image
        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        width: 120
        height: 120
        source: ""
    }

    Row {
        anchors.top: image.bottom
        anchors.horizontalCenter: parent.horizontalCenter

        Button {
            id: button1
            text: "Condition1 = " + condition1
            onClicked: condition1 = !condition1
        }
        Button {
            id: button2
            text: "Condition2 = " + condition2
            onClicked: condition2 = !condition2
        }
        Button {
            id: button3
            text: "Condition3 = " + condition3
            onClicked: condition3 = !condition3
        }
    }

    StateObserver{
        condition: condition1 && condition2 && condition3
        bitmap: "Images/1.png"
        target: image
    }
    StateObserver{
        condition: !condition1 && condition2 && condition3
        bitmap: "Images/2.png"
        target: image
    }
    StateObserver{
        condition: !condition1 && !condition2 && condition3
        bitmap: "Images/3.png"
        target: image
    }
}

StateObserver.qml

import QtQuick 2.0

Item {

    property bool condition

    property string bitmap

    property var target

    onConditionChanged: {
        if (condition){
        target.source = bitmap
        }
        else {
            target.source = ""
        }

        console.log("Change bitmap: " + bitmap)
    }
}

Upvotes: 2

Views: 16066

Answers (2)

F-Kae
F-Kae

Reputation: 155

By the comment of dtech I have created an example using states. This works like expected and I am able to separate the states(requirements) from the main.qml(Layout). Thanks!

main.qml:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0

ApplicationWindow {
    id: mainApp
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    property bool condition1: false
    property bool condition2: false
    property bool condition3: false

    Image{
        id: image

        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        width: 120
        height: 120

        Img_StateList{
            container: image
        }
    }


    Row {
        anchors.top: image.bottom
        anchors.horizontalCenter: parent.horizontalCenter

        Button {
            id: button1
            text: "Condition1 = " + condition1
            onClicked: condition1 = !condition1
        }
        Button {
            id: button2
            text: "Condition2 = " + condition2
            onClicked: condition2 = !condition2
        }
        Button {
            id: button3
            text: "Condition3 = " + condition3
            onClicked: condition3 = !condition3
        }
    }
}

Img_StateList.qml:

import QtQuick 2.0

Item {
    property Image container;
    state: "default"
    states: [
        State {
            name: "default"
            PropertyChanges {
                target: container
                source: ''
            }
        },
        State {
            name: "Img1"
            when: condition1 && condition2 && condition3
            PropertyChanges {
                target: container
                source: 'Images/1.png'
            }
        },
        State {
            name: "Img2"
            when: !condition1 && condition2 && condition3
            PropertyChanges {
                target: container
                source: 'Images/2.png'
            }
        },
        State {
            name: "Img3"
            when: !condition1 && !condition2 && condition3
            PropertyChanges {
                target: container
                source: 'Images/3.png'
            }
        }
    ]
}

Upvotes: 0

dtech
dtech

Reputation: 49329

I would not consider this a sound approach, as the order of binding expression evaluation is unspecified.

What I would do is have a single point that evaluates all possible combinations and use its result to rig the rest of the properties.

Through your StateObserver you are basically introducing unnecessary complexity. You also don't account for every possible state configuration.

You could simply:

  Image {
    id: image
    //...
    source: {
      if (condition1 && condition2 && condition3) return "Images/1.png"
      else if (!condition1 && condition2 && condition3) return "Images/2.png"
      else if (!condition1 && !condition2 && condition3) return "Images/3.png"
      else return ""
    } 
  }

That's less code and less objects, meaning lower ram and cpu usage.

But if you insist on using your current approach, you can use Binding elements which can automatically toggle on or off using their when property.

Finally, you can also use QML's own state machine, although for such a trivial case that would be overkill.

Upvotes: 2

Related Questions