Skip to content

New in Qt 5.10: Diagnostics when breaking QML bindings

Property bindings are one of the most interesting features of the QML language. In QML, when we set a value on a property, the right hand side expression isn’t evaluated just once to produce a value, like in a ordinary imperative language.

In particular, if the expression involves other properties, then the property we’re setting becomes bound to the properties in the expression: whenever they change their values, then the expression is automatically evaluated again, and the target property value updated.

For instance:

import QtQuick 2.0

Rectangle {
    width: 100; height: 100

    // this is a binding:
    color: mouseArea.containsMouse ? "red" : "blue"

    MouseArea { id: mouseArea; anchors.fill: parent; hoverEnabled: true }
}

In the snippet above, the color property of the Rectangle is the target of a binding: it is bound to the containsMouse property of the internal MouseArea. When the mouse moves inside or outside of the mouse area, its containsMouse property will change, causing the expression for color to be re-evaluated.

In layman’s terms: the rectangle will be red if the mouse cursor is hovering over it, and blue otherwise.

Property bindings allow to build our UIs in a very declarative way: we don’t need to write boilerplate “slots” to update our UI elements when some other property changes value. The expression that we can write on the right hand side can be as complex as we want, even involving function calls; the engine will do all the work for us, and will make the binding work as expected.

Breaking property bindings

Sounds too good to be true? Well, property bindings have a limitation: if we use an imperative assignment to set a value on a property, its binding will be lost. For instance, suppose we want the rectangle to turn green when we click over it. This is a possible implementation:

import QtQuick 2.0

Rectangle {
    width: 100; height: 100

    // this is still a binding
    color: mouseArea.containsMouse ? "red" : "blue"

    MouseArea { 
        id: mouseArea
        anchors.fill: parent 
        hoverEnabled: true 
        onClicked: {
            parent.color = "green" // oops! breaks binding
        }
    }
}

In the onClicked signal handler we set the color property to green using an ordinary assignment from imperative JavaScript code. With this code, if we click on the rectangle, and then move the mouse over it and outside it, we’ll see that the rectangle doesn’t change color any more — it stays green. This happens because the imperative assignment destroyed the binding, and set the color property to one specific value.

Are broken bindings a problem?

In general, the above example is 100% correct QML code, following the documented QML semantics. Therefore, one cannot claim that such code should be rejected by the QML engine or unconditionally raise warnings.

However, in my experience at KDAB, I have never encountered one single occasion when breaking a binding was intended by the developer. Broken bindings were always caused by accident (during refactorings, not realizing that the original property was bound to something, etc.), or by excessive usage of JavaScript to manage state, instead of using more declarative approaches.

On some occasions, a silent overwriting of a binding with a value did actually introduce a runtime bug, which would then stay latent, maybe for a long time. Debugging such issues could be very challenging, as it is not normally clear what the problem is when reading the QML source code — the bug would manifest itself only when the JavaScript code containing an imperative assignment is run.

Debugging broken bindings

In order for a developer to be able to more easily track down these cases, KDAB contributed a new feature to the QML engine that will appear in Qt 5.10: the QML engine can now print debugging information whenever a binding is broken.

In order to enable the debug output you just need to enable output for the qt.qml.binding.removal logging category, for instance by exporting the QT_LOGGING_RULES environment variable:

export QT_LOGGING_RULES="qt.qml.binding.removal.info=true"

With this variable set, the above example:

import QtQuick 2.0

Rectangle {
    width: 100; height: 100

    color: mouseArea.containsMouse ? "red" : "blue"

    MouseArea { 
        id: mouseArea
        anchors.fill: parent 
        hoverEnabled: true 
        onClicked: {
            parent.color = "green" 
        }
    }
}

now prints this debug message:

qt.qml.binding.removal: Overwriting binding on QQuickRectangle::color
    at file:///example.qml:13
    that was initially bound at file:///example.qml:6:12

As you can see, the message tells us which property was bound, where the binding had been established, and where the imperative statement is that breaks the binding. Pretty much everything we need to debug any issues with broken bindings!

About KDAB

KDAB is a consulting company offering a wide variety of expert services in Qt, C++ and 3D/OpenGL and providing training courses in:

KDAB believes that it is critical for our business to contribute to the Qt framework and C++ thinking, to keep pushing these technologies forward to ensure they remain competitive.

Categories: KDAB Blogs / KDAB on Qt / QML

2 thoughts on “New in Qt 5.10: Diagnostics when breaking QML bindings”

  1. Yeah…looks really good. But I think you should not try to use declarative approach at all costs. Sometimes a simple imperative call saves a lot of debugging unwanted behaviour after adjusting a complex property binding.

    However, can`t wait to try it with our complex application. Hopefully there are not too much “qt.qml.binding.removal:” prints ;-).

    1. > Hopefully there are not too much “qt.qml.binding.removal:” prints ;-).

      Hopefully there are not too much breaking bindings in the libraries, so you can focus on the points where you break the bindings your self.

      It would be awesome if you could add some annotation to disable this (and other warnings, such as binding loop warnings) if there are few specific points where it is intended.

      How will it behave if I *override (possibly temporarily)* the binding, using a Binding object?

Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close