From 2368a183e162734bf10657baad97f66cfd0ac602 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 16 Feb 2022 15:42:21 -0800 Subject: [PATCH] Qt6: prevent QML object from being garbage collected It appears that the QML object that is created when assigning the source to the StatsView in the ui can be garbage collected and therefore destroyed and re-instantiated without our knowledge. So instead of trying to keep a pointer around, we end up looking up the object address when needed. We still ask the JS code to not garbage collect the object - but that clearly isn't enough to prevent it from being destroyed when the parent widget changes. Without this fix opening the statistics will crash with Qt6. Signed-off-by: Dirk Hohndel --- desktop-widgets/statswidget.cpp | 33 +++++++++++++++++++++++++-------- desktop-widgets/statswidget.h | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/desktop-widgets/statswidget.cpp b/desktop-widgets/statswidget.cpp index 9441fe3f2..aded3891e 100644 --- a/desktop-widgets/statswidget.cpp +++ b/desktop-widgets/statswidget.cpp @@ -5,6 +5,7 @@ #include #include #include +#include class ChartItemDelegate : public QStyledItemDelegate { private: @@ -90,12 +91,24 @@ StatsWidget::StatsWidget(QWidget *parent) : QWidget(parent) ui.stats->setSource(urlStatsView); ui.stats->setResizeMode(QQuickWidget::SizeRootObjectToView); - QQuickItem *root = ui.stats->rootObject(); - view = qobject_cast(root); + (void)getView(); +} + +// hack around the Qt6 bug where the QML object gets destroyed and recreated +StatsView *StatsWidget::getView() +{ + StatsView *view = qobject_cast(ui.stats->rootObject()); if (!view) qWarning("Oops. The root of the StatsView is not a StatsView."); - if (view) + if (view) { + // try to prevent the JS garbage collection from freeing the object + // this appears to fail with Qt6 which is why we still look up the + // object from the ui.stats rootObject + ui.stats->engine()->setObjectOwnership(view, QQmlEngine::CppOwnership); + view->setParent(this); view->setVisible(isVisible()); // Synchronize visibility of widget and QtQuick-view. + } + return view; } // Initialize QComboBox with list of variables @@ -143,13 +156,14 @@ void StatsWidget::updateUi() connect(check, &QCheckBox::stateChanged, [this,id] (int state) { featureChanged(id, state); }); ui.features->addWidget(check); } - + StatsView *view = getView(); if (view) view->plot(state); } void StatsWidget::updateRestrictionLabel() { + StatsView *view = getView(); if (!view) return; int num = view->restrictionCount(); @@ -211,6 +225,7 @@ void StatsWidget::featureChanged(int idx, bool status) { state.featureChanged(idx, status); // No need for a full chart replot - just show/hide the features + StatsView *view = getView(); if (view) view->updateFeatures(state); } @@ -221,20 +236,21 @@ void StatsWidget::showEvent(QShowEvent *e) updateUi(); QWidget::showEvent(e); // Apparently, we have to manage the visibility of the view ourselves. That's mad. - if (view) - view->setVisible(true); + // this is implicitly done in getView() - so we can ignore the return value + (void)getView(); } void StatsWidget::hideEvent(QHideEvent *e) { QWidget::hideEvent(e); // Apparently, we have to manage the visibility of the view ourselves. That's mad. - if (view) - view->setVisible(false); + // this is implicitly done in getView() - so we can ignore the return value + (void)getView(); } void StatsWidget::restrict() { + StatsView *view = getView(); if (view) view->restrictToSelection(); updateRestrictionLabel(); @@ -242,6 +258,7 @@ void StatsWidget::restrict() void StatsWidget::unrestrict() { + StatsView *view = getView(); if (view) view->unrestrict(); updateRestrictionLabel(); diff --git a/desktop-widgets/statswidget.h b/desktop-widgets/statswidget.h index 93378d373..9b1a36ae5 100644 --- a/desktop-widgets/statswidget.h +++ b/desktop-widgets/statswidget.h @@ -31,7 +31,7 @@ slots: private: Ui::StatsWidget ui; StatsState state; - StatsView *view; + StatsView *getView(); void updateUi(); void updateRestrictionLabel(); std::vector> features;