This may have gotten fixed by the fix for QTBUG-107850[1] (commit 7487332)[2]. QTBUG-84858 gets a tacit mention there, but is not among the list of duplicates that got closed through this (even though it probably should be).
I hastily backported this to 5.15.8 (see attachment), but have not yet checked if it actually compiles (the test might use functions/macros missing from 5.15) or works. I can't look into this more currently since I'm somewhat busy. Hopefully I can try it out this weekend. Regards. 1: https://bugreports.qt.io/browse/QTBUG-107850 2: https://invent.kde.org/qt/qt/qtdeclarative/-/commit/74873324bdf3399753f9fcaf7461c0e00df628b1
Description: backport commit 7487332 to hopefully fix #983597 Edited from the original for 6.5.0 FF: https://invent.kde.org/qt/qt/qtdeclarative/-/commit/74873324bdf3399753f9fcaf7461c0e00df628b1 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2326,6 +2326,7 @@ QQuickItem::~QQuickItem() { Q_D(QQuickItem); + d->inDestructor = true; if (d->windowRefCount > 1) d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow(). @@ -2689,9 +2690,8 @@ const bool wasVisible = isVisible(); op->removeChild(this); - if (wasVisible) { + if (wasVisible && !op->inDestructor) emit oldParentItem->visibleChildrenChanged(); - } } else if (d->window) { QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this); } @@ -2768,8 +2768,9 @@ d->itemChange(ItemParentHasChanged, d->parentItem); - emit parentChanged(d->parentItem); - if (isVisible() && d->parentItem) + if (!d->inDestructor) + emit parentChanged(d->parentItem); + if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor) emit d->parentItem->visibleChildrenChanged(); } @@ -2965,7 +2966,8 @@ itemChange(QQuickItem::ItemChildRemovedChange, child); - emit q->childrenChanged(); + if (!inDestructor) + emit q->childrenChanged(); } void QQuickItemPrivate::refWindow(QQuickWindow *c) @@ -3194,6 +3196,7 @@ , touchEnabled(false) #endif , hasCursorHandler(false) + , inDestructor(false) , dirtyAttributes(0) , nextDirtyItem(nullptr) , prevDirtyItem(nullptr) @@ -6106,9 +6109,11 @@ QAccessible::updateAccessibility(&ev); } #endif - emit q->visibleChanged(); - if (childVisibilityChanged) - emit q->visibleChildrenChanged(); + if (!inDestructor) { + emit q->visibleChanged(); + if (childVisibilityChanged) + emit q->visibleChildrenChanged(); + } return true; // effective visibility DID change } --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -472,6 +472,7 @@ bool replayingPressEvent:1; bool touchEnabled:1; bool hasCursorHandler:1; + quint32 inDestructor:1; // has entered ~QQuickItem enum DirtyType { TransformOrigin = 0x00000001, --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -130,6 +130,7 @@ void isAncestorOf(); void grab(); + void signalsOnDestruction(); private: QQmlEngine engine; @@ -3520,6 +3521,52 @@ QVERIFY(!sub2.isAncestorOf(&sub2)); } +/* + Items that are getting destroyed should not emit property change notifications. +*/ +void tst_QQuickItem::signalsOnDestruction() +{ + QQuickWindow window; + window.show(); + + // visual children, but not QObject children + std::unique_ptr<QQuickItem> parent(new QQuickItem(window.contentItem())); + std::unique_ptr<QQuickItem> child(new QQuickItem); + std::unique_ptr<QQuickItem> grandChild(new QQuickItem); + + QSignalSpy childrenSpy(parent.get(), &QQuickItem::childrenChanged); + QSignalSpy visibleChildrenSpy(parent.get(), &QQuickItem::visibleChildrenChanged); + QSignalSpy childParentSpy(child.get(), &QQuickItem::parentChanged); + QSignalSpy childChildrenSpy(child.get(), &QQuickItem::childrenChanged); + QSignalSpy childVisibleChildrenSpy(child.get(), &QQuickItem::visibleChanged); + QSignalSpy grandChildParentSpy(grandChild.get(), &QQuickItem::parentChanged); + + child->setParentItem(parent.get()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), 1); + QCOMPARE(childParentSpy.count(), 1); + QCOMPARE(childChildrenSpy.count(), 0); + QCOMPARE(childVisibleChildrenSpy.count(), 0); + + grandChild->setParentItem(child.get()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), 1); + QCOMPARE(childParentSpy.count(), 1); + QCOMPARE(childChildrenSpy.count(), 1); + QCOMPARE(childVisibleChildrenSpy.count(), 0); + QCOMPARE(grandChildParentSpy.count(), 1); + + parent.reset(); + + QVERIFY(!child->parentItem()); + QVERIFY(grandChild->parentItem()); + QCOMPARE(childrenSpy.count(), 1); + QCOMPARE(visibleChildrenSpy.count(), 1); + QCOMPARE(childParentSpy.count(), 2); + QCOMPARE(grandChildParentSpy.count(), 1); +} + + QTEST_MAIN(tst_QQuickItem) #include "tst_qquickitem.moc"