如何分析"绑定循环"

时间:2016-07-05 06:33:29

标签: qt qml

我有一个带有C ++模型和QML可视化的Qt / QML应用程序。

在运行时(启动),我收到警告

  

QML项目:为属性" xyz"

检测到绑定循环

我的QML中没有明显的循环。 我可以启用更多调试来了解此循环的来源吗?其他建议?

2 个答案:

答案 0 :(得分:8)

我通常通过在打印警告的Qt代码中放置断点来实现此目的。为此,您需要一个带调试符号的Qt。

搜索"检测到绑定循环"在Qt来源中给了我QQmlAbstractBinding::printBindingLoopError()。在那里放置断点通常会导致回溯,从而清楚地了解情况。

更新:David Edmundson开发了一个小工具,在绑定循环上显示仅QML回溯,请参阅his blog here。在引擎盖下完全按照这里描述的那样,只是它很好地自动化并包装在Python脚本中。

示例:

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

回溯:

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    

在回溯中,可以看到加载文件时计算子项的anchors.fill锚点(第35,36帧)。这会导致子项的宽度发生变化(第31帧),从而导致绑定更新(第25帧),以便绑定"宽度"父项目上的属性(第17帧)。这又强制重新计算儿童锚(框架14),其改变儿童的宽度(框架10),其更新绑定(框架4)。这是在第25帧中已经更新的相同绑定,因此存在绑定循环。可以看到第25帧和第4帧中的this指针是相同的,即相同的绑定是递归更新的。

答案 1 :(得分:3)

非常感谢收据,但是收据并没有帮助我。万一有人需要它,请添加另一个可能的解决方案。我在ListView中遇到绑定循环,试图将所有项目宽度和列表宽度设置为项目最大值:

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

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

绑定循环错误出现在项目计数更新上,但不是每次都出现-仅在某些批处理绑定更新上出现,而没有实际的绑定循环。通过将绑定表达式移动到Binding QML Type并为其添加delayed属性,可以解决此问题:

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
    }
}

所以我的情况是:

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
    }
}