Skip to content

Commit e479b71

Browse files
committed
[bugfix] Do not crash when layer is destroyed while loading
Fixes #17636 QGIS crashes when a layer is removed while loading the attribute table
1 parent 53ebe05 commit e479b71

File tree

5 files changed

+85
-48
lines changed

5 files changed

+85
-48
lines changed

python/gui/attributetable/qgsdualview.sip

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ class QgsDualView : QStackedWidget
208208
%Docstring
209209
Copy the content of the selected cell in the clipboard.
210210
.. versionadded:: 1.16
211+
%End
212+
213+
void cancelProgress( );
214+
%Docstring
215+
Cancel the progress dialog (if any)
216+
.. versionadded:: 3.0
211217
%End
212218

213219
signals:

src/app/qgsattributetabledialog.cpp

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ QgsExpressionContext QgsAttributeTableDialog::createExpressionContext() const
6969

7070
void QgsAttributeTableDialog::updateMultiEditButtonState()
7171
{
72-
if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::UiFileLayout )
72+
if ( ! mLayer || ( mLayer->editFormConfig().layout() == QgsEditFormConfig::UiFileLayout ) )
7373
return;
7474

7575
mActionToggleMultiEdit->setEnabled( mLayer->isEditable() );
@@ -199,7 +199,7 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *layer, QgsAttr
199199
// info from layer to table
200200
connect( mLayer, &QgsVectorLayer::editingStarted, this, &QgsAttributeTableDialog::editingToggled );
201201
connect( mLayer, &QgsVectorLayer::editingStopped, this, &QgsAttributeTableDialog::editingToggled );
202-
connect( mLayer, &QObject::destroyed, this, &QWidget::close );
202+
connect( mLayer, &QObject::destroyed, mMainView, &QgsDualView::cancelProgress );
203203
connect( mLayer, &QgsVectorLayer::selectionChanged, this, &QgsAttributeTableDialog::updateTitle );
204204
connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsAttributeTableDialog::updateTitle );
205205
connect( mLayer, &QgsVectorLayer::featuresDeleted, this, &QgsAttributeTableDialog::updateTitle );
@@ -292,59 +292,69 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *layer, QgsAttr
292292
break;
293293
}
294294

295-
mUpdateExpressionText->registerExpressionContextGenerator( this );
296-
mFieldCombo->setFilters( QgsFieldProxyModel::AllTypes | QgsFieldProxyModel::HideReadOnly );
297-
mFieldCombo->setLayer( mLayer );
298-
299-
connect( mRunFieldCalc, &QAbstractButton::clicked, this, &QgsAttributeTableDialog::updateFieldFromExpression );
300-
connect( mRunFieldCalcSelected, &QAbstractButton::clicked, this, &QgsAttributeTableDialog::updateFieldFromExpressionSelected );
301-
// NW TODO Fix in 2.6 - Doesn't work with field model for some reason.
302-
// connect( mUpdateExpressionText, SIGNAL( returnPressed() ), this, SLOT( updateFieldFromExpression() ) );
303-
connect( mUpdateExpressionText, static_cast < void ( QgsFieldExpressionWidget::* )( const QString &, bool ) > ( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsAttributeTableDialog::updateButtonStatus );
304-
mUpdateExpressionText->setLayer( mLayer );
305-
mUpdateExpressionText->setLeftHandButtonStyle( true );
306-
307-
int initialView = settings.value( QStringLiteral( "qgis/attributeTableView" ), -1 ).toInt();
308-
if ( initialView < 0 )
295+
// Layer might have been destroyed while loading!
296+
if ( mLayer )
309297
{
310-
initialView = settings.value( QStringLiteral( "qgis/attributeTableLastView" ), QgsDualView::AttributeTable ).toInt();
311-
}
312-
mMainView->setView( static_cast< QgsDualView::ViewMode >( initialView ) );
313-
mMainViewButtonGroup->button( initialView )->setChecked( true );
298+
mUpdateExpressionText->registerExpressionContextGenerator( this );
299+
mFieldCombo->setFilters( QgsFieldProxyModel::AllTypes | QgsFieldProxyModel::HideReadOnly );
300+
mFieldCombo->setLayer( mLayer );
301+
302+
connect( mRunFieldCalc, &QAbstractButton::clicked, this, &QgsAttributeTableDialog::updateFieldFromExpression );
303+
connect( mRunFieldCalcSelected, &QAbstractButton::clicked, this, &QgsAttributeTableDialog::updateFieldFromExpressionSelected );
304+
// NW TODO Fix in 2.6 - Doesn't work with field model for some reason.
305+
// connect( mUpdateExpressionText, SIGNAL( returnPressed() ), this, SLOT( updateFieldFromExpression() ) );
306+
connect( mUpdateExpressionText, static_cast < void ( QgsFieldExpressionWidget::* )( const QString &, bool ) > ( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsAttributeTableDialog::updateButtonStatus );
307+
mUpdateExpressionText->setLayer( mLayer );
308+
mUpdateExpressionText->setLeftHandButtonStyle( true );
309+
310+
int initialView = settings.value( QStringLiteral( "qgis/attributeTableView" ), -1 ).toInt();
311+
if ( initialView < 0 )
312+
{
313+
initialView = settings.value( QStringLiteral( "qgis/attributeTableLastView" ), QgsDualView::AttributeTable ).toInt();
314+
}
315+
mMainView->setView( static_cast< QgsDualView::ViewMode >( initialView ) );
316+
mMainViewButtonGroup->button( initialView )->setChecked( true );
314317

315-
connect( mActionToggleMultiEdit, &QAction::toggled, mMainView, &QgsDualView::setMultiEditEnabled );
316-
connect( mActionSearchForm, &QAction::toggled, mMainView, &QgsDualView::toggleSearchMode );
317-
updateMultiEditButtonState();
318+
connect( mActionToggleMultiEdit, &QAction::toggled, mMainView, &QgsDualView::setMultiEditEnabled );
319+
connect( mActionSearchForm, &QAction::toggled, mMainView, &QgsDualView::toggleSearchMode );
320+
updateMultiEditButtonState();
318321

319-
if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::UiFileLayout )
320-
{
321-
//not supported with custom UI
322-
mActionToggleMultiEdit->setEnabled( false );
323-
mActionToggleMultiEdit->setToolTip( tr( "Multiedit is not supported when using custom UI forms" ) );
324-
mActionSearchForm->setEnabled( false );
325-
mActionSearchForm->setToolTip( tr( "Search is not supported when using custom UI forms" ) );
326-
}
322+
if ( mLayer->editFormConfig().layout() == QgsEditFormConfig::UiFileLayout )
323+
{
324+
//not supported with custom UI
325+
mActionToggleMultiEdit->setEnabled( false );
326+
mActionToggleMultiEdit->setToolTip( tr( "Multiedit is not supported when using custom UI forms" ) );
327+
mActionSearchForm->setEnabled( false );
328+
mActionSearchForm->setToolTip( tr( "Search is not supported when using custom UI forms" ) );
329+
}
327330

328-
QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Layer" ) );
331+
QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Layer" ) );
329332

330-
if ( actions.isEmpty() )
331-
{
332-
mActionFeatureActions->setVisible( false );
333+
if ( actions.isEmpty() )
334+
{
335+
mActionFeatureActions->setVisible( false );
336+
}
337+
else
338+
{
339+
QMenu *actionMenu = new QMenu();
340+
Q_FOREACH ( const QgsAction &action, actions )
341+
{
342+
QAction *qAction = actionMenu->addAction( action.icon(), action.shortTitle() );
343+
qAction->setToolTip( action.name() );
344+
qAction->setData( QVariant::fromValue<QgsAction>( action ) );
345+
connect( qAction, &QAction::triggered, this, &QgsAttributeTableDialog::layerActionTriggered );
346+
}
347+
mActionFeatureActions->setMenu( actionMenu );
348+
}
349+
350+
editingToggled();
351+
// Close and delete if the layer has been destroyed
352+
connect( mLayer, &QObject::destroyed, this, &QWidget::close );
333353
}
334354
else
335355
{
336-
QMenu *actionMenu = new QMenu();
337-
Q_FOREACH ( const QgsAction &action, actions )
338-
{
339-
QAction *qAction = actionMenu->addAction( action.icon(), action.shortTitle() );
340-
qAction->setToolTip( action.name() );
341-
qAction->setData( QVariant::fromValue<QgsAction>( action ) );
342-
connect( qAction, &QAction::triggered, this, &QgsAttributeTableDialog::layerActionTriggered );
343-
}
344-
mActionFeatureActions->setMenu( actionMenu );
356+
QWidget::close();
345357
}
346-
347-
editingToggled();
348358
}
349359

350360
QgsAttributeTableDialog::~QgsAttributeTableDialog()
@@ -354,6 +364,10 @@ QgsAttributeTableDialog::~QgsAttributeTableDialog()
354364

355365
void QgsAttributeTableDialog::updateTitle()
356366
{
367+
if ( ! mLayer )
368+
{
369+
return;
370+
}
357371
QWidget *w = mDock ? qobject_cast<QWidget *>( mDock ) : qobject_cast<QWidget *>( this );
358372
w->setWindowTitle( tr( " %1 :: Features Total: %2, Filtered: %3, Selected: %4" )
359373
.arg( mLayer->name() )

src/app/qgsattributetabledialog.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
230230
QMenu *mFilterColumnsMenu = nullptr;
231231
QSignalMapper *mFilterActionMapper = nullptr;
232232

233-
QgsVectorLayer *mLayer = nullptr;
233+
QPointer< QgsVectorLayer > mLayer = nullptr;
234234
QgsSearchWidgetWrapper *mCurrentSearchWidgetWrapper = nullptr;
235235
QStringList mVisibleFields;
236236
QgsAttributeEditorContext mEditorContext;

src/gui/attributetable/qgsdualview.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ void QgsDualView::restoreRecentDisplayExpressions()
318318

319319
void QgsDualView::saveRecentDisplayExpressions() const
320320
{
321+
if ( ! mLayer )
322+
{
323+
return;
324+
}
321325
QList<QAction *> actions = mFeatureListPreviewButton->actions();
322326

323327
// Remove existing same action
@@ -397,6 +401,7 @@ void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression
397401
mLastDisplayExpressionAction = previewAction;
398402
}
399403

404+
400405
void QgsDualView::mFeatureList_aboutToChangeEditSelection( bool &ok )
401406
{
402407
if ( mLayer->isEditable() && !mAttributeForm->save() )
@@ -515,6 +520,12 @@ void QgsDualView::copyCellContent() const
515520
}
516521
}
517522

523+
void QgsDualView::cancelProgress()
524+
{
525+
if ( mProgressDlg )
526+
mProgressDlg->cancel();
527+
}
528+
518529
void QgsDualView::hideEvent( QHideEvent *event )
519530
{
520531
Q_UNUSED( event )

src/gui/attributetable/qgsdualview.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
236236
*/
237237
void copyCellContent() const;
238238

239+
/**
240+
* Cancel the progress dialog (if any)
241+
* \since QGIS 3.0
242+
*/
243+
void cancelProgress( );
244+
239245
signals:
240246

241247
/**
@@ -360,7 +366,7 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
360366
QAction *mLastDisplayExpressionAction = nullptr;
361367
QMenu *mHorizontalHeaderMenu = nullptr;
362368
QgsVectorLayerCache *mLayerCache = nullptr;
363-
QgsVectorLayer *mLayer = nullptr;
369+
QPointer< QgsVectorLayer > mLayer = nullptr;
364370
QProgressDialog *mProgressDlg = nullptr;
365371
QgsIFeatureSelectionManager *mFeatureSelectionManager = nullptr;
366372
QgsDistanceArea mDistanceArea;

0 commit comments

Comments
 (0)