Skip to content

Commit deee8e2

Browse files
committed
[FEATURE] Composer map to follow a visibility preset (fixes #13418)
This adds a new option in composer map properties: "Follow visibility preset" with a combo box to choose the active preset. This is an alternative to "lock layers" (and "lock layer styles") functionality which would just copy preset's configuration, while the new option links to preset. The difference is that when a preset is updated, composer map will automatically pick the new configuration when following the preset, while there is no update if "lock layers" (and "lock layer styles") option is used.
1 parent 8c402bc commit deee8e2

File tree

6 files changed

+255
-101
lines changed

6 files changed

+255
-101
lines changed

python/core/composer/qgscomposermap.sip

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,27 @@ class QgsComposerMap : QgsComposerItem
223223
/** Stores the current layer styles into style overrides. @note added in 2.8 */
224224
void storeCurrentLayerStyles();
225225

226+
/** Whether the map should follow a visibility preset. If true, the layers and layer styles
227+
* will be used from given preset name (configured with setFollowVisibilityPresetName() method).
228+
* This means when preset's settings are changed, the new settings are automatically
229+
* picked up next time when rendering, without having to explicitly update them.
230+
* At most one of the flags keepLayerSet() and followVisibilityPreset() should be enabled
231+
* at any time since they are alternative approaches - if both are enabled,
232+
* following visibility preset has higher priority. If neither is enabled (or if preset name is not set),
233+
* map will use the same configuration as the map canvas uses.
234+
* @note added in 2.16 */
235+
bool followVisibilityPreset() const;
236+
/** Sets whether the map should follow a visibility preset. See followVisibilityPreset() for more details.
237+
* @note added in 2.16 */
238+
void setFollowVisibilityPreset( bool follow );
239+
/** Preset name that decides which layers and layer styles are used for map rendering. It is only
240+
* used when followVisibilityPreset() returns true.
241+
* @note added in 2.16 */
242+
QString followVisibilityPresetName() const;
243+
/** Sets preset name for map rendering. See followVisibilityPresetName() for more details.
244+
* @note added in 2.16 */
245+
void setFollowVisibilityPresetName( const QString& name );
246+
226247
// Set cache outdated
227248
void setCacheUpdated( bool u = false );
228249

src/app/composer/qgscomposermapwidget.cpp

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,19 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap )
125125
//set initial state of frame style controls
126126
toggleFrameControls( false, false, false );
127127

128-
QMenu* m = new QMenu( this );
129-
mLayerListFromPresetButton->setMenu( m );
128+
// follow preset combo
129+
mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) );
130+
connect( mFollowVisibilityPresetCombo, SIGNAL( currentIndexChanged( int ) ), this, SLOT( followVisibilityPresetSelected( int ) ) );
131+
connect( QgsProject::instance()->visibilityPresetCollection(), SIGNAL( presetsChanged() ),
132+
this, SLOT( onPresetsChanged() ) );
133+
onPresetsChanged();
134+
135+
// keep layers from preset button
136+
QMenu* menuKeepLayers = new QMenu( this );
137+
mLayerListFromPresetButton->setMenu( menuKeepLayers );
130138
mLayerListFromPresetButton->setIcon( QgsApplication::getThemeIcon( "/mActionShowAllLayers.png" ) );
131139
mLayerListFromPresetButton->setToolTip( tr( "Set layer list from a visibility preset" ) );
132-
133-
connect( m, SIGNAL( aboutToShow() ), this, SLOT( aboutToShowVisibilityPresetsMenu() ) );
140+
connect( menuKeepLayers, SIGNAL( aboutToShow() ), this, SLOT( aboutToShowKeepLayersVisibilityPresetsMenu() ) );
134141

135142
if ( composerMap )
136143
{
@@ -301,28 +308,47 @@ void QgsComposerMapWidget::compositionAtlasToggled( bool atlasEnabled )
301308
}
302309
}
303310

304-
void QgsComposerMapWidget::aboutToShowVisibilityPresetsMenu()
311+
void QgsComposerMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu()
305312
{
313+
// this menu is for the case when setting "keep layers" and "keep layer styles"
314+
// and the preset configuration is copied. The preset is not followed further.
315+
306316
QMenu* menu = qobject_cast<QMenu*>( sender() );
307317
if ( !menu )
308318
return;
309319

310320
menu->clear();
311321
Q_FOREACH ( const QString& presetName, QgsProject::instance()->visibilityPresetCollection()->presets() )
312322
{
313-
QAction* a = menu->addAction( presetName, this, SLOT( visibilityPresetSelected() ) );
314-
a->setCheckable( true );
315-
QStringList layers = QgsVisibilityPresets::instance()->orderedPresetVisibleLayers( presetName );
316-
QMap<QString, QString> styles = QgsProject::instance()->visibilityPresetCollection()->presetStyleOverrides( presetName );
317-
if ( layers == mComposerMap->layerSet() && styles == mComposerMap->layerStyleOverrides() )
318-
a->setChecked( true );
323+
menu->addAction( presetName, this, SLOT( keepLayersVisibilityPresetSelected() ) );
319324
}
320325

321326
if ( menu->actions().isEmpty() )
322327
menu->addAction( tr( "No presets defined" ) )->setEnabled( false );
323328
}
324329

325-
void QgsComposerMapWidget::visibilityPresetSelected()
330+
void QgsComposerMapWidget::followVisibilityPresetSelected( int currentIndex )
331+
{
332+
if ( !mComposerMap )
333+
return;
334+
335+
QString presetName;
336+
if ( currentIndex != 0 )
337+
{
338+
presetName = mFollowVisibilityPresetCombo->currentText();
339+
}
340+
341+
if ( presetName == mComposerMap->followVisibilityPresetName() )
342+
return;
343+
344+
mFollowVisibilityPresetCheckBox->setChecked( true );
345+
mComposerMap->setFollowVisibilityPresetName( presetName );
346+
347+
mComposerMap->cache();
348+
mComposerMap->update();
349+
}
350+
351+
void QgsComposerMapWidget::keepLayersVisibilityPresetSelected()
326352
{
327353
QAction* action = qobject_cast<QAction*>( sender() );
328354
if ( !action )
@@ -344,6 +370,17 @@ void QgsComposerMapWidget::visibilityPresetSelected()
344370
}
345371
}
346372

373+
void QgsComposerMapWidget::onPresetsChanged()
374+
{
375+
if ( QStringListModel* model = qobject_cast<QStringListModel*>( mFollowVisibilityPresetCombo->model() ) )
376+
{
377+
QStringList lst;
378+
lst.append( tr( "(none)" ) );
379+
lst += QgsProject::instance()->visibilityPresetCollection()->presets();
380+
model->setStringList( lst );
381+
}
382+
}
383+
347384
void QgsComposerMapWidget::on_mAtlasCheckBox_toggled( bool checked )
348385
{
349386
if ( !mComposerMap )
@@ -668,6 +705,12 @@ void QgsComposerMapWidget::updateGuiElements()
668705

669706
mMapRotationSpinBox->setValue( mComposerMap->mapRotation( QgsComposerObject::OriginalValue ) );
670707

708+
// follow preset check box
709+
mFollowVisibilityPresetCheckBox->setCheckState(
710+
mComposerMap->followVisibilityPreset() ? Qt::Checked : Qt::Unchecked );
711+
int presetModelIndex = mFollowVisibilityPresetCombo->findText( mComposerMap->followVisibilityPresetName() );
712+
mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none
713+
671714
//keep layer list check box
672715
if ( mComposerMap->keepLayerSet() )
673716
{
@@ -897,6 +940,30 @@ void QgsComposerMapWidget::on_mUpdatePreviewButton_clicked()
897940
mUpdatePreviewButton->setEnabled( true );
898941
}
899942

943+
void QgsComposerMapWidget::on_mFollowVisibilityPresetCheckBox_stateChanged( int state )
944+
{
945+
if ( !mComposerMap )
946+
{
947+
return;
948+
}
949+
950+
if ( state == Qt::Checked )
951+
{
952+
mComposerMap->setFollowVisibilityPreset( true );
953+
954+
// mutually exclusive with keeping custom layer list
955+
mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
956+
mKeepLayerStylesCheckBox->setCheckState( Qt::Unchecked );
957+
958+
mComposerMap->cache();
959+
mComposerMap->update();
960+
}
961+
else
962+
{
963+
mComposerMap->setFollowVisibilityPreset( false );
964+
}
965+
}
966+
900967
void QgsComposerMapWidget::on_mKeepLayerListCheckBox_stateChanged( int state )
901968
{
902969
if ( !mComposerMap )
@@ -908,6 +975,9 @@ void QgsComposerMapWidget::on_mKeepLayerListCheckBox_stateChanged( int state )
908975
{
909976
mComposerMap->storeCurrentLayerSet();
910977
mComposerMap->setKeepLayerSet( true );
978+
979+
// mutually exclusive with following a preset
980+
mFollowVisibilityPresetCheckBox->setCheckState( Qt::Unchecked );
911981
}
912982
else
913983
{

src/app/composer/qgscomposermapwidget.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
4343
void on_mSetToMapCanvasExtentButton_clicked();
4444
void on_mViewExtentInCanvasButton_clicked();
4545
void on_mUpdatePreviewButton_clicked();
46+
void on_mFollowVisibilityPresetCheckBox_stateChanged( int state );
4647
void on_mKeepLayerListCheckBox_stateChanged( int state );
4748
void on_mKeepLayerStylesCheckBox_stateChanged( int state );
4849
void on_mDrawCanvasItemsCheckBox_stateChanged( int state );
@@ -168,9 +169,12 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
168169
/** Enables or disables the atlas controls when composer atlas is toggled on/off*/
169170
void compositionAtlasToggled( bool atlasEnabled );
170171

171-
void aboutToShowVisibilityPresetsMenu();
172+
void aboutToShowKeepLayersVisibilityPresetsMenu();
172173

173-
void visibilityPresetSelected();
174+
void followVisibilityPresetSelected( int currentIndex );
175+
void keepLayersVisibilityPresetSelected();
176+
177+
void onPresetsChanged();
174178

175179
private:
176180
QgsComposerMap* mComposerMap;

src/core/composer/qgscomposermap.cpp

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
5353
, mEvaluatedMapRotation( 0 )
5454
, mKeepLayerSet( false )
5555
, mKeepLayerStyles( false )
56+
, mFollowVisibilityPreset( false )
5657
, mUpdatesEnabled( true )
5758
, mMapCanvas( nullptr )
5859
, mDrawCanvasItems( true )
@@ -98,6 +99,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
9899
, mEvaluatedMapRotation( 0 )
99100
, mKeepLayerSet( false )
100101
, mKeepLayerStyles( false )
102+
, mFollowVisibilityPreset( false )
101103
, mUpdatesEnabled( true )
102104
, mMapCanvas( nullptr )
103105
, mDrawCanvasItems( true )
@@ -537,28 +539,32 @@ QStringList QgsComposerMap::layersToRender( const QgsExpressionContext* context
537539

538540
QStringList renderLayerSet;
539541

540-
QVariant exprVal;
541-
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, *evalContext ) )
542+
if ( mFollowVisibilityPreset )
542543
{
543-
QString presetName = exprVal.toString();
544+
QString presetName = mFollowVisibilityPresetName;
545+
546+
// preset name can be overridden by data-defined one
547+
QVariant exprVal;
548+
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, *evalContext ) )
549+
{
550+
presetName = exprVal.toString();
551+
}
544552

545553
if ( QgsProject::instance()->visibilityPresetCollection()->hasPreset( presetName ) )
546554
renderLayerSet = QgsProject::instance()->visibilityPresetCollection()->presetVisibleLayers( presetName );
555+
else // fallback to using map canvas layers
556+
renderLayerSet = mComposition->mapSettings().layers();
547557
}
548-
549-
//use stored layer set or read current set from main canvas
550-
if ( renderLayerSet.isEmpty() )
558+
else if ( mKeepLayerSet )
551559
{
552-
if ( mKeepLayerSet )
553-
{
554-
renderLayerSet = mLayerSet;
555-
}
556-
else
557-
{
558-
renderLayerSet = mComposition->mapSettings().layers();
559-
}
560+
renderLayerSet = mLayerSet;
561+
}
562+
else
563+
{
564+
renderLayerSet = mComposition->mapSettings().layers();
560565
}
561566

567+
QVariant exprVal;
562568
if ( dataDefinedEvaluate( QgsComposerObject::MapLayers, exprVal, *evalContext ) )
563569
{
564570
renderLayerSet.clear();
@@ -595,16 +601,29 @@ QStringList QgsComposerMap::layersToRender( const QgsExpressionContext* context
595601

596602
QMap<QString, QString> QgsComposerMap::layerStyleOverridesToRender( const QgsExpressionContext& context ) const
597603
{
598-
QVariant exprVal;
599-
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, context ) )
604+
if ( mFollowVisibilityPreset )
600605
{
601-
QString presetName = exprVal.toString();
606+
QString presetName = mFollowVisibilityPresetName;
607+
608+
QVariant exprVal;
609+
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, context ) )
610+
{
611+
presetName = exprVal.toString();
612+
}
602613

603614
if ( QgsProject::instance()->visibilityPresetCollection()->hasPreset( presetName ) )
604615
return QgsProject::instance()->visibilityPresetCollection()->presetStyleOverrides( presetName );
605-
616+
else
617+
return QMap<QString, QString>();
618+
}
619+
else if ( mKeepLayerStyles )
620+
{
621+
return mLayerStyleOverrides;
622+
}
623+
else
624+
{
625+
return QMap<QString, QString>();
606626
}
607-
return mLayerStyleOverrides;
608627
}
609628

610629
double QgsComposerMap::scale() const
@@ -1294,6 +1313,10 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
12941313
extentElem.setAttribute( "ymax", qgsDoubleToString( mExtent.yMaximum() ) );
12951314
composerMapElem.appendChild( extentElem );
12961315

1316+
// follow visibility preset
1317+
composerMapElem.setAttribute( "followPreset", mFollowVisibilityPreset ? "true" : "false" );
1318+
composerMapElem.setAttribute( "followPresetName", mFollowVisibilityPresetName );
1319+
12971320
//map rotation
12981321
composerMapElem.setAttribute( "mapRotation", QString::number( mMapRotation ) );
12991322

@@ -1395,6 +1418,10 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
13951418
mMapRotation = itemElem.attribute( "mapRotation", "0" ).toDouble();
13961419
}
13971420

1421+
// follow visibility preset
1422+
mFollowVisibilityPreset = itemElem.attribute( "followPreset" ).compare( "true" ) == 0;
1423+
mFollowVisibilityPresetName = itemElem.attribute( "followPresetName" );
1424+
13981425
//mKeepLayerSet flag
13991426
QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
14001427
if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )

src/core/composer/qgscomposermap.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,27 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
262262
/** Stores the current layer styles into style overrides. @note added in 2.8 */
263263
void storeCurrentLayerStyles();
264264

265+
/** Whether the map should follow a visibility preset. If true, the layers and layer styles
266+
* will be used from given preset name (configured with setFollowVisibilityPresetName() method).
267+
* This means when preset's settings are changed, the new settings are automatically
268+
* picked up next time when rendering, without having to explicitly update them.
269+
* At most one of the flags keepLayerSet() and followVisibilityPreset() should be enabled
270+
* at any time since they are alternative approaches - if both are enabled,
271+
* following visibility preset has higher priority. If neither is enabled (or if preset name is not set),
272+
* map will use the same configuration as the map canvas uses.
273+
* @note added in 2.16 */
274+
bool followVisibilityPreset() const { return mFollowVisibilityPreset; }
275+
/** Sets whether the map should follow a visibility preset. See followVisibilityPreset() for more details.
276+
* @note added in 2.16 */
277+
void setFollowVisibilityPreset( bool follow ) { mFollowVisibilityPreset = follow; }
278+
/** Preset name that decides which layers and layer styles are used for map rendering. It is only
279+
* used when followVisibilityPreset() returns true.
280+
* @note added in 2.16 */
281+
QString followVisibilityPresetName() const { return mFollowVisibilityPresetName; }
282+
/** Sets preset name for map rendering. See followVisibilityPresetName() for more details.
283+
* @note added in 2.16 */
284+
void setFollowVisibilityPresetName( const QString& name ) { mFollowVisibilityPresetName = name; }
285+
265286
// Set cache outdated
266287
void setCacheUpdated( bool u = false );
267288

@@ -888,6 +909,14 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
888909
/** Stored style names (value) to be used with particular layer IDs (key) instead of default style */
889910
QMap<QString, QString> mLayerStyleOverrides;
890911

912+
/** Whether layers and styles should be used from a preset (preset name is stored
913+
* in mVisibilityPresetName and may be overridden by data-defined expression).
914+
* This flag has higher priority than mKeepLayerSet. */
915+
bool mFollowVisibilityPreset;
916+
/** Visibility preset name to be used for map's layers and styles in case mFollowVisibilityPreset
917+
* is true. May be overridden by data-defined expression. */
918+
QString mFollowVisibilityPresetName;
919+
891920
/** Whether updates to the map are enabled */
892921
bool mUpdatesEnabled;
893922

0 commit comments

Comments
 (0)