How to analyse a "Binding loop"
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
}
}
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.