Skip to content

Commit 42517f8

Browse files
committedApr 11, 2018
[FEATURE] Mark layers as required in the project
Required layers are not allowed to be removed from the project. This adds extra safety to protect project users from removing layers they may think are not needed (e.g. used in joins, relations, expressions). Users can set/unset layers that are required in project properties dialog.
1 parent 3400199 commit 42517f8

File tree

7 files changed

+139
-3
lines changed

7 files changed

+139
-3
lines changed
 

‎python/core/qgsproject.sip.in

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,26 @@ Sets the project's ``metadata`` store.
952952
.. seealso:: :py:func:`metadata`
953953

954954
.. seealso:: :py:func:`metadataChanged`
955+
%End
956+
957+
QSet<QgsMapLayer *> requiredLayers() const;
958+
%Docstring
959+
Returns a set of map layers that are required in the project and therefore they should not get
960+
removed from the project. The set of layers may be configured by users in project properties.
961+
and it is mainly a hint for the user interface to protect users from removing layers that important
962+
in the project. The removeMapLayer(), removeMapLayers() calls do not block removal of layers listed here.
963+
964+
.. versionadded:: 3.2
965+
%End
966+
967+
void setRequiredLayers( const QSet<QgsMapLayer *> &layers );
968+
%Docstring
969+
Configures a set of map layers that are required in the project and therefore they should not get
970+
removed from the project. The set of layers may be configured by users in project properties.
971+
and it is mainly a hint for the user interface to protect users from removing layers that important
972+
in the project. The removeMapLayer(), removeMapLayers() calls do not block removal of layers listed here.
973+
974+
.. versionadded:: 3.2
955975
%End
956976

957977
signals:

‎src/app/qgisapp.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9298,15 +9298,17 @@ void QgisApp::removeLayer()
92989298
return;
92999299
}
93009300

9301-
Q_FOREACH ( QgsMapLayer *layer, mLayerTreeView->selectedLayers() )
9301+
const QList<QgsMapLayer *> selectedLayers = mLayerTreeView->selectedLayers();
9302+
9303+
for ( QgsMapLayer *layer : selectedLayers )
93029304
{
93039305
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
93049306
if ( vlayer && vlayer->isEditable() && !toggleEditing( vlayer, true ) )
93059307
return;
93069308
}
93079309

93089310
QStringList activeTaskDescriptions;
9309-
Q_FOREACH ( QgsMapLayer *layer, mLayerTreeView->selectedLayers() )
9311+
for ( QgsMapLayer *layer : selectedLayers )
93109312
{
93119313
QList< QgsTask * > tasks = QgsApplication::taskManager()->tasksDependentOnLayer( layer );
93129314
if ( !tasks.isEmpty() )
@@ -9318,6 +9320,18 @@ void QgisApp::removeLayer()
93189320
}
93199321
}
93209322

9323+
const QSet<QgsMapLayer *> requiredLayers = QgsProject::instance()->requiredLayers();
9324+
for ( QgsMapLayer *layer : selectedLayers )
9325+
{
9326+
if ( requiredLayers.contains( layer ) )
9327+
{
9328+
QMessageBox::warning( this, tr( "Required layers" ),
9329+
tr( "The layer '%1' is marked as a required and therefore it cannot be removed from the project.\n\n"
9330+
"If you really need to remove the layer, unmark it as required in the Project Properties window > Data Sources tab." ).arg( layer->name() ) );
9331+
return;
9332+
}
9333+
}
9334+
93219335
if ( !activeTaskDescriptions.isEmpty() )
93229336
{
93239337
QMessageBox::warning( this, tr( "Active Tasks" ),

‎src/app/qgsprojectproperties.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,7 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
831831
connect( titleEdit, &QLineEdit::textChanged, mMetadataWidget, &QgsMetadataWidget::setTitle );
832832

833833
projectionSelectorInitialized();
834+
populateRequiredLayers();
834835
restoreOptionsBaseUi();
835836
restoreState();
836837
}
@@ -1283,6 +1284,8 @@ void QgsProjectProperties::apply()
12831284
canvas->refresh();
12841285
}
12851286
QgisApp::instance()->mapOverviewCanvas()->refresh();
1287+
1288+
applyRequiredLayers();
12861289
}
12871290

12881291
void QgsProjectProperties::showProjectionsTab()
@@ -2122,3 +2125,37 @@ void QgsProjectProperties::showHelp()
21222125
}
21232126
QgsHelp::openHelp( link );
21242127
}
2128+
2129+
void QgsProjectProperties::populateRequiredLayers()
2130+
{
2131+
const QSet<QgsMapLayer *> requiredLayers = QgsProject::instance()->requiredLayers();
2132+
QStandardItemModel *model = new QStandardItemModel( mViewRequiredLayers );
2133+
QList<QgsLayerTreeLayer *> layers = QgsProject::instance()->layerTreeRoot()->findLayers();
2134+
std::sort( layers.begin(), layers.end(), []( QgsLayerTreeLayer * layer1, QgsLayerTreeLayer * layer2 ) { return layer1->name() < layer2->name(); } );
2135+
for ( const QgsLayerTreeLayer *l : layers )
2136+
{
2137+
QStandardItem *item = new QStandardItem( l->name() );
2138+
item->setCheckable( true );
2139+
item->setCheckState( requiredLayers.contains( l->layer() ) ? Qt::Checked : Qt::Unchecked );
2140+
item->setData( l->layerId() );
2141+
model->appendRow( item );
2142+
}
2143+
2144+
mViewRequiredLayers->setModel( model );
2145+
}
2146+
2147+
void QgsProjectProperties::applyRequiredLayers()
2148+
{
2149+
QSet<QgsMapLayer *> requiredLayers;
2150+
QAbstractItemModel *model = mViewRequiredLayers->model();
2151+
for ( int i = 0; i < model->rowCount(); ++i )
2152+
{
2153+
if ( model->data( model->index( i, 0 ), Qt::CheckStateRole ).toInt() == Qt::Checked )
2154+
{
2155+
QString layerId = model->data( model->index( i, 0 ), Qt::UserRole + 1 ).toString();
2156+
if ( QgsMapLayer *layer = QgsProject::instance()->mapLayer( layerId ) )
2157+
requiredLayers << layer;
2158+
}
2159+
}
2160+
QgsProject::instance()->setRequiredLayers( requiredLayers );
2161+
}

‎src/app/qgsprojectproperties.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,7 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui:
224224
void updateGuiForMapUnits();
225225

226226
void showHelp();
227+
228+
void populateRequiredLayers();
229+
void applyRequiredLayers();
227230
};

‎src/core/qgsproject.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,3 +2660,25 @@ void QgsProject::setMetadata( const QgsProjectMetadata &metadata )
26602660

26612661
setDirty( true );
26622662
}
2663+
2664+
QSet<QgsMapLayer *> QgsProject::requiredLayers() const
2665+
{
2666+
QSet<QgsMapLayer *> layers;
2667+
const QStringList lst = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
2668+
for ( const QString &layerId : lst )
2669+
{
2670+
if ( QgsMapLayer *layer = mapLayer( layerId ) )
2671+
layers.insert( layer );
2672+
}
2673+
return layers;
2674+
}
2675+
2676+
void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
2677+
{
2678+
QStringList layerIds;
2679+
for ( QgsMapLayer *layer : layers )
2680+
{
2681+
layerIds << layer->id();
2682+
}
2683+
writeEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ), layerIds );
2684+
}

‎src/core/qgsproject.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,24 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
928928
*/
929929
void setMetadata( const QgsProjectMetadata &metadata );
930930

931+
/**
932+
* Returns a set of map layers that are required in the project and therefore they should not get
933+
* removed from the project. The set of layers may be configured by users in project properties.
934+
* and it is mainly a hint for the user interface to protect users from removing layers that important
935+
* in the project. The removeMapLayer(), removeMapLayers() calls do not block removal of layers listed here.
936+
* \since QGIS 3.2
937+
*/
938+
QSet<QgsMapLayer *> requiredLayers() const;
939+
940+
/**
941+
* Configures a set of map layers that are required in the project and therefore they should not get
942+
* removed from the project. The set of layers may be configured by users in project properties.
943+
* and it is mainly a hint for the user interface to protect users from removing layers that important
944+
* in the project. The removeMapLayer(), removeMapLayers() calls do not block removal of layers listed here.
945+
* \since QGIS 3.2
946+
*/
947+
void setRequiredLayers( const QSet<QgsMapLayer *> &layers );
948+
931949
signals:
932950
//! emitted when project is being read
933951
void readProject( const QDomDocument & );

‎src/ui/qgsprojectpropertiesbase.ui

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,7 @@
14431443
</property>
14441444
</widget>
14451445
</item>
1446-
<item row="2" column="0">
1446+
<item row="3" column="0">
14471447
<widget class="QCheckBox" name="mTrustProjectCheckBox">
14481448
<property name="toolTip">
14491449
<string>Speed up project loading by skipping data checks. Useful in qgis server context or project with huge database views or materialized views.</string>
@@ -1453,6 +1453,28 @@
14531453
</property>
14541454
</widget>
14551455
</item>
1456+
<item row="4" column="0" colspan="2">
1457+
<widget class="QgsCollapsibleGroupBox" name="groupBox_5">
1458+
<property name="title">
1459+
<string>Required layers</string>
1460+
</property>
1461+
<layout class="QGridLayout" name="gridLayout_19">
1462+
<item row="0" column="0" colspan="2">
1463+
<widget class="QLabel" name="label_31">
1464+
<property name="text">
1465+
<string>Checked layers in this list are protected from inadvertent removal from the project.</string>
1466+
</property>
1467+
<property name="wordWrap">
1468+
<bool>true</bool>
1469+
</property>
1470+
</widget>
1471+
</item>
1472+
<item row="2" column="0" colspan="2">
1473+
<widget class="QListView" name="mViewRequiredLayers"/>
1474+
</item>
1475+
</layout>
1476+
</widget>
1477+
</item>
14561478
</layout>
14571479
</widget>
14581480
<widget class="QWidget" name="mTabRelations">

0 commit comments

Comments
 (0)
Please sign in to comment.