From 486007e013629a0a55dc2a3b861e1cfb21fd4910 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 24 Sep 2021 00:11:17 +0200 Subject: [PATCH] QgisApp::closeProject(): cancel canvas jobs before closing the project, to avoid rendering jobs to access deleted objects (fixes #44144) --- python/gui/auto_generated/qgsmapcanvas.sip.in | 1 + src/app/qgisapp.cpp | 1 + src/gui/qgsmapcanvas.cpp | 29 ++++++++++++------- src/gui/qgsmapcanvas.h | 6 ++++ 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/python/gui/auto_generated/qgsmapcanvas.sip.in b/python/gui/auto_generated/qgsmapcanvas.sip.in index 0ac4476ca461..a0a7bbbeeded 100644 --- a/python/gui/auto_generated/qgsmapcanvas.sip.in +++ b/python/gui/auto_generated/qgsmapcanvas.sip.in @@ -133,6 +133,7 @@ Make sure to remove any rendered images from cache (does nothing if cache is not .. versionadded:: 2.4 %End + void waitWhileRendering(); %Docstring Blocks until the rendering job has finished. diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 3b77ac47532b..b4f6548ec2d8 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -13387,6 +13387,7 @@ void QgisApp::closeProject() // clear out any stuff from project mMapCanvas->setLayers( QList() ); mMapCanvas->clearCache(); + mMapCanvas->cancelJobs(); mOverviewCanvas->setLayers( QList() ); // Avoid unnecessary layer changed handling for each layer removed - instead, diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index 2c35cf631dd7..ee5972ae115c 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -242,12 +242,30 @@ QgsMapCanvas::~QgsMapCanvas() } mLastNonZoomMapTool = nullptr; + cancelJobs(); + + // delete canvas items prior to deleting the canvas + // because they might try to update canvas when it's + // already being destructed, ends with segfault + qDeleteAll( mScene->items() ); + + mScene->deleteLater(); // crashes in python tests on windows + + delete mCache; + delete mLabelingResults; +} + + +void QgsMapCanvas::cancelJobs() +{ + // rendering job may still end up writing into canvas map item // so kill it before deleting canvas items if ( mJob ) { whileBlocking( mJob )->cancel(); delete mJob; + mJob = nullptr; } QList< QgsMapRendererQImageJob * >::const_iterator previewJob = mPreviewJobs.constBegin(); @@ -259,18 +277,9 @@ QgsMapCanvas::~QgsMapCanvas() delete *previewJob; } } - - // delete canvas items prior to deleting the canvas - // because they might try to update canvas when it's - // already being destructed, ends with segfault - qDeleteAll( mScene->items() ); - - mScene->deleteLater(); // crashes in python tests on windows - - delete mCache; - delete mLabelingResults; } + void QgsMapCanvas::setMagnificationFactor( double factor, const QgsPointXY *center ) { // do not go higher or lower than min max magnification ratio diff --git a/src/gui/qgsmapcanvas.h b/src/gui/qgsmapcanvas.h index 6bb56a8b8cb2..e45c7582cd76 100644 --- a/src/gui/qgsmapcanvas.h +++ b/src/gui/qgsmapcanvas.h @@ -183,6 +183,12 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView, public QgsExpressionContex */ void clearCache(); + /** + * Cancel any rendering job, in a blocking way. Used for application closing. + * \note not available in Python bindings + */ + void cancelJobs() SIP_SKIP; + /** * Blocks until the rendering job has finished. *