Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to analyse a "Binding loop"

Tags:

qt

qml

I have a Qt/QML application with a C++ model and a QML visualisation.

At run-time (start-up), I get a warning

QML Item: Binding loop detected for property "xyz"

I see no obvious loop in my QML. Can I enable more debugging to understand where this loop comes from? Other suggestions?

like image 705
Marc Van Daele Avatar asked Jul 05 '16 06:07

Marc Van Daele


2 Answers

Thanks much for the receipt however it did not help me. In case somebody will need it, adding another possible solution. I was getting binding loops in ListView trying to set all items width and list width to item max value:

ListView {
    implicitWidth: contentItem.childrenRect.width
    delegate: listItem
}

Item {
    id: listItem
    width: Math.max(internalWidth, listView.implicitWidth)
}

Binding loop error appeared on items count update but not every time - only on some batch binding updates, while the is no actual binding loop. Was able to solve the issue by moving binding expression to Binding QML Type and adding delayed property to it:

Item { // Item causing binding loop
    Binding on item_property_causing_loop {
        value: <binding_expression>
        when: <when_expression> // Optional however could also help
        delayed: true  // Prevent intermediary values from being assigned
    }
}

So in my case it is:

Item { // Item causing binding loop
    id: listItem
    Binding on width {
        value: Math.max(internalWidth, listView.implicitWidth)
        when: index >= 0 // Optional however could also help
        delayed: true    // Prevent intermediary values from being assigned
    }
}
like image 132
Aleksey Kontsevich Avatar answered Oct 06 '22 00:10

Aleksey Kontsevich


I usually do this by placing a breakpoint in the Qt code that prints the warning. For that, you need to have a Qt with debug symbols.

Searching for "Binding loop detected" in the Qt sources gives me QQmlAbstractBinding::printBindingLoopError(). Placing a breakpoint there will usually lead to a backtrace that gives a clear picture of the situation.

Update: David Edmundson has developed a little tool that displays a QML-only backtrace on binding loops, see his blog here. Under the hood is does exactly what is described here, only that it is nicely automated and wrapped in a Python script.

Example:

Rectangle {
    id: parent
    width: child.width + 1
    height: child.height + 1
    Rectangle {
        id: child
        anchors.fill: parent
    }
}

Backtrace:

1   QQmlAbstractBinding::printBindingLoopError  qqmlabstractbinding.cpp 178 0x7ffff6eb36da  
2   QQmlBinding::update qqmlbinding.cpp 221 0x7ffff6eb9abe  
3   QQmlBinding::update qqmlbinding_p.h 97  0x7ffff6eba354  
4   QQmlBinding::expressionChanged  qqmlbinding.cpp 260 0x7ffff6eb9e68  
5   QQmlJavaScriptExpressionGuard_callback  qqmljavascriptexpression.cpp    361 0x7ffff6eb223e  
6   QQmlNotifier::emitNotify    qqmlnotifier.cpp    94  0x7ffff6e9087a  
7   QQmlData::signalEmitted qqmlengine.cpp  763 0x7ffff6e19a45  
8   QMetaObject::activate   qobject.cpp 3599    0x7ffff683655e  
9   QMetaObject::activate   qobject.cpp 3578    0x7ffff6836364  
10  QQuickItem::widthChanged    moc_qquickitem.cpp  1104    0x7ffff7a7ba49  
11  QQuickItem::geometryChanged qquickitem.cpp  3533    0x7ffff7a6e9cd  
12  QQuickItem::setSize qquickitem.cpp  6389    0x7ffff7a75f35  
13  QQuickAnchorsPrivate::setItemSize   qquickanchors.cpp   400 0x7ffff7a60d94  
14  QQuickAnchorsPrivate::fillChanged   qquickanchors.cpp   177 0x7ffff7a5fe0e  
15  QQuickAnchorsPrivate::itemGeometryChanged   qquickanchors.cpp   441 0x7ffff7a6106f  
16  QQuickItem::geometryChanged qquickitem.cpp  3523    0x7ffff7a6e96c  
17  QQuickItem::setWidth    qquickitem.cpp  6091    0x7ffff7a74c1d  
18  QQuickItem::qt_static_metacall  moc_qquickitem.cpp  874 0x7ffff7a7b0dc  
19  QQuickItem::qt_metacall moc_qquickitem.cpp  946 0x7ffff7a7b4d8  
20  QQuickRectangle::qt_metacall    moc_qquickrectangle_p.cpp   610 0x7ffff7c189c2  
21  QMetaObject::metacall   qmetaobject.cpp 296 0x7ffff680118b  
22  QQmlPropertyPrivate::writeBinding   qqmlproperty.cpp    1512    0x7ffff6e33ec3  
23  QQmlBinding::update qqmlbinding.cpp 199 0x7ffff6eb992a  
24  QQmlBinding::update qqmlbinding_p.h 97  0x7ffff6eba354  
25  QQmlBinding::expressionChanged  qqmlbinding.cpp 260 0x7ffff6eb9e68  
26  QQmlJavaScriptExpressionGuard_callback  qqmljavascriptexpression.cpp    361 0x7ffff6eb223e  
27  QQmlNotifier::emitNotify    qqmlnotifier.cpp    94  0x7ffff6e9087a  
28  QQmlData::signalEmitted qqmlengine.cpp  763 0x7ffff6e19a45  
29  QMetaObject::activate   qobject.cpp 3599    0x7ffff683655e  
30  QMetaObject::activate   qobject.cpp 3578    0x7ffff6836364  
31  QQuickItem::widthChanged    moc_qquickitem.cpp  1104    0x7ffff7a7ba49  
32  QQuickItem::geometryChanged qquickitem.cpp  3533    0x7ffff7a6e9cd  
33  QQuickItem::setSize qquickitem.cpp  6389    0x7ffff7a75f35  
34  QQuickAnchorsPrivate::setItemSize   qquickanchors.cpp   400 0x7ffff7a60d94  
35  QQuickAnchorsPrivate::fillChanged   qquickanchors.cpp   177 0x7ffff7a5fe0e  
36  QQuickAnchorsPrivate::update    qquickanchors.cpp   431 0x7ffff7a60fc6  
37  QQuickAnchorsPrivate::updateOnComplete  qquickanchors.cpp   425 0x7ffff7a60f93  
38  QQuickItem::componentComplete   qquickitem.cpp  4593    0x7ffff7a70944  
39  QQmlObjectCreator::finalize qqmlobjectcreator.cpp   1207    0x7ffff6ecab66  
40  QQmlComponentPrivate::complete  qqmlcomponent.cpp   928 0x7ffff6e38609  
41  QQmlComponentPrivate::completeCreate    qqmlcomponent.cpp   964 0x7ffff6e386ee  
42  QQmlComponent::completeCreate   qqmlcomponent.cpp   957 0x7ffff6e386a0  
43  QQmlComponent::create   qqmlcomponent.cpp   791 0x7ffff6e37edd  
44  QQuickView::continueExecute qquickview.cpp  476 0x7ffff7b720d4  
45  QQuickViewPrivate::execute  qquickview.cpp  124 0x7ffff7b7101f  
46  QQuickView::setSource   qquickview.cpp  253 0x7ffff7b71426  
47  main    main.cpp    24  0x4033e4    

In the backtrace, one can see that the anchors.fill anchor for the child item is calculated when loading the file (frame 35, 36). That causes the child item's width to change (frame 31), which causes a binding update (frame 25) for a binding on the "width" property (frame 17) on the parent item. That in turn forces a recalculation of the child anchors (frame 14), which changes the child's width (frame 10), which updates a binding (frame 4). That is the same binding that was already being updated in frame 25, hence a binding loop exists. One can see that the this pointer in frame 25 and frame 4 are the same, i.e. the same binding is updated recursively.

like image 38
Thomas McGuire Avatar answered Oct 05 '22 23:10

Thomas McGuire