Skip to content

Commit

Permalink
[FEATURE] Composer map to follow a visibility preset (fixes #13418)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wonder-sk committed May 17, 2016
1 parent 8c402bc commit deee8e2
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 101 deletions.
21 changes: 21 additions & 0 deletions python/core/composer/qgscomposermap.sip
Expand Up @@ -223,6 +223,27 @@ class QgsComposerMap : QgsComposerItem
/** Stores the current layer styles into style overrides. @note added in 2.8 */
void storeCurrentLayerStyles();

/** Whether the map should follow a visibility preset. If true, the layers and layer styles
* will be used from given preset name (configured with setFollowVisibilityPresetName() method).
* This means when preset's settings are changed, the new settings are automatically
* picked up next time when rendering, without having to explicitly update them.
* At most one of the flags keepLayerSet() and followVisibilityPreset() should be enabled
* at any time since they are alternative approaches - if both are enabled,
* following visibility preset has higher priority. If neither is enabled (or if preset name is not set),
* map will use the same configuration as the map canvas uses.
* @note added in 2.16 */
bool followVisibilityPreset() const;
/** Sets whether the map should follow a visibility preset. See followVisibilityPreset() for more details.
* @note added in 2.16 */
void setFollowVisibilityPreset( bool follow );
/** Preset name that decides which layers and layer styles are used for map rendering. It is only
* used when followVisibilityPreset() returns true.
* @note added in 2.16 */
QString followVisibilityPresetName() const;
/** Sets preset name for map rendering. See followVisibilityPresetName() for more details.
* @note added in 2.16 */
void setFollowVisibilityPresetName( const QString& name );

// Set cache outdated
void setCacheUpdated( bool u = false );

Expand Down
94 changes: 82 additions & 12 deletions src/app/composer/qgscomposermapwidget.cpp
Expand Up @@ -125,12 +125,19 @@ QgsComposerMapWidget::QgsComposerMapWidget( QgsComposerMap* composerMap )
//set initial state of frame style controls
toggleFrameControls( false, false, false );

QMenu* m = new QMenu( this );
mLayerListFromPresetButton->setMenu( m );
// follow preset combo
mFollowVisibilityPresetCombo->setModel( new QStringListModel( mFollowVisibilityPresetCombo ) );
connect( mFollowVisibilityPresetCombo, SIGNAL( currentIndexChanged( int ) ), this, SLOT( followVisibilityPresetSelected( int ) ) );
connect( QgsProject::instance()->visibilityPresetCollection(), SIGNAL( presetsChanged() ),
this, SLOT( onPresetsChanged() ) );
onPresetsChanged();

// keep layers from preset button
QMenu* menuKeepLayers = new QMenu( this );
mLayerListFromPresetButton->setMenu( menuKeepLayers );
mLayerListFromPresetButton->setIcon( QgsApplication::getThemeIcon( "/mActionShowAllLayers.png" ) );
mLayerListFromPresetButton->setToolTip( tr( "Set layer list from a visibility preset" ) );

connect( m, SIGNAL( aboutToShow() ), this, SLOT( aboutToShowVisibilityPresetsMenu() ) );
connect( menuKeepLayers, SIGNAL( aboutToShow() ), this, SLOT( aboutToShowKeepLayersVisibilityPresetsMenu() ) );

if ( composerMap )
{
Expand Down Expand Up @@ -301,28 +308,47 @@ void QgsComposerMapWidget::compositionAtlasToggled( bool atlasEnabled )
}
}

void QgsComposerMapWidget::aboutToShowVisibilityPresetsMenu()
void QgsComposerMapWidget::aboutToShowKeepLayersVisibilityPresetsMenu()
{
// this menu is for the case when setting "keep layers" and "keep layer styles"
// and the preset configuration is copied. The preset is not followed further.

QMenu* menu = qobject_cast<QMenu*>( sender() );
if ( !menu )
return;

menu->clear();
Q_FOREACH ( const QString& presetName, QgsProject::instance()->visibilityPresetCollection()->presets() )
{
QAction* a = menu->addAction( presetName, this, SLOT( visibilityPresetSelected() ) );
a->setCheckable( true );
QStringList layers = QgsVisibilityPresets::instance()->orderedPresetVisibleLayers( presetName );
QMap<QString, QString> styles = QgsProject::instance()->visibilityPresetCollection()->presetStyleOverrides( presetName );
if ( layers == mComposerMap->layerSet() && styles == mComposerMap->layerStyleOverrides() )
a->setChecked( true );
menu->addAction( presetName, this, SLOT( keepLayersVisibilityPresetSelected() ) );
}

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

void QgsComposerMapWidget::visibilityPresetSelected()
void QgsComposerMapWidget::followVisibilityPresetSelected( int currentIndex )
{
if ( !mComposerMap )
return;

QString presetName;
if ( currentIndex != 0 )
{
presetName = mFollowVisibilityPresetCombo->currentText();
}

if ( presetName == mComposerMap->followVisibilityPresetName() )
return;

mFollowVisibilityPresetCheckBox->setChecked( true );
mComposerMap->setFollowVisibilityPresetName( presetName );

mComposerMap->cache();
mComposerMap->update();
}

void QgsComposerMapWidget::keepLayersVisibilityPresetSelected()
{
QAction* action = qobject_cast<QAction*>( sender() );
if ( !action )
Expand All @@ -344,6 +370,17 @@ void QgsComposerMapWidget::visibilityPresetSelected()
}
}

void QgsComposerMapWidget::onPresetsChanged()
{
if ( QStringListModel* model = qobject_cast<QStringListModel*>( mFollowVisibilityPresetCombo->model() ) )
{
QStringList lst;
lst.append( tr( "(none)" ) );
lst += QgsProject::instance()->visibilityPresetCollection()->presets();
model->setStringList( lst );
}
}

void QgsComposerMapWidget::on_mAtlasCheckBox_toggled( bool checked )
{
if ( !mComposerMap )
Expand Down Expand Up @@ -668,6 +705,12 @@ void QgsComposerMapWidget::updateGuiElements()

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

// follow preset check box
mFollowVisibilityPresetCheckBox->setCheckState(
mComposerMap->followVisibilityPreset() ? Qt::Checked : Qt::Unchecked );
int presetModelIndex = mFollowVisibilityPresetCombo->findText( mComposerMap->followVisibilityPresetName() );
mFollowVisibilityPresetCombo->setCurrentIndex( presetModelIndex != -1 ? presetModelIndex : 0 ); // 0 == none

//keep layer list check box
if ( mComposerMap->keepLayerSet() )
{
Expand Down Expand Up @@ -897,6 +940,30 @@ void QgsComposerMapWidget::on_mUpdatePreviewButton_clicked()
mUpdatePreviewButton->setEnabled( true );
}

void QgsComposerMapWidget::on_mFollowVisibilityPresetCheckBox_stateChanged( int state )
{
if ( !mComposerMap )
{
return;
}

if ( state == Qt::Checked )
{
mComposerMap->setFollowVisibilityPreset( true );

// mutually exclusive with keeping custom layer list
mKeepLayerListCheckBox->setCheckState( Qt::Unchecked );
mKeepLayerStylesCheckBox->setCheckState( Qt::Unchecked );

mComposerMap->cache();
mComposerMap->update();
}
else
{
mComposerMap->setFollowVisibilityPreset( false );
}
}

void QgsComposerMapWidget::on_mKeepLayerListCheckBox_stateChanged( int state )
{
if ( !mComposerMap )
Expand All @@ -908,6 +975,9 @@ void QgsComposerMapWidget::on_mKeepLayerListCheckBox_stateChanged( int state )
{
mComposerMap->storeCurrentLayerSet();
mComposerMap->setKeepLayerSet( true );

// mutually exclusive with following a preset
mFollowVisibilityPresetCheckBox->setCheckState( Qt::Unchecked );
}
else
{
Expand Down
8 changes: 6 additions & 2 deletions src/app/composer/qgscomposermapwidget.h
Expand Up @@ -43,6 +43,7 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
void on_mSetToMapCanvasExtentButton_clicked();
void on_mViewExtentInCanvasButton_clicked();
void on_mUpdatePreviewButton_clicked();
void on_mFollowVisibilityPresetCheckBox_stateChanged( int state );
void on_mKeepLayerListCheckBox_stateChanged( int state );
void on_mKeepLayerStylesCheckBox_stateChanged( int state );
void on_mDrawCanvasItemsCheckBox_stateChanged( int state );
Expand Down Expand Up @@ -168,9 +169,12 @@ class QgsComposerMapWidget: public QgsComposerItemBaseWidget, private Ui::QgsCom
/** Enables or disables the atlas controls when composer atlas is toggled on/off*/
void compositionAtlasToggled( bool atlasEnabled );

void aboutToShowVisibilityPresetsMenu();
void aboutToShowKeepLayersVisibilityPresetsMenu();

void visibilityPresetSelected();
void followVisibilityPresetSelected( int currentIndex );
void keepLayersVisibilityPresetSelected();

void onPresetsChanged();

private:
QgsComposerMap* mComposerMap;
Expand Down
65 changes: 46 additions & 19 deletions src/core/composer/qgscomposermap.cpp
Expand Up @@ -53,6 +53,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mKeepLayerStyles( false )
, mFollowVisibilityPreset( false )
, mUpdatesEnabled( true )
, mMapCanvas( nullptr )
, mDrawCanvasItems( true )
Expand Down Expand Up @@ -98,6 +99,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mKeepLayerStyles( false )
, mFollowVisibilityPreset( false )
, mUpdatesEnabled( true )
, mMapCanvas( nullptr )
, mDrawCanvasItems( true )
Expand Down Expand Up @@ -537,28 +539,32 @@ QStringList QgsComposerMap::layersToRender( const QgsExpressionContext* context

QStringList renderLayerSet;

QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, *evalContext ) )
if ( mFollowVisibilityPreset )
{
QString presetName = exprVal.toString();
QString presetName = mFollowVisibilityPresetName;

// preset name can be overridden by data-defined one
QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, *evalContext ) )
{
presetName = exprVal.toString();
}

if ( QgsProject::instance()->visibilityPresetCollection()->hasPreset( presetName ) )
renderLayerSet = QgsProject::instance()->visibilityPresetCollection()->presetVisibleLayers( presetName );
else // fallback to using map canvas layers
renderLayerSet = mComposition->mapSettings().layers();
}

//use stored layer set or read current set from main canvas
if ( renderLayerSet.isEmpty() )
else if ( mKeepLayerSet )
{
if ( mKeepLayerSet )
{
renderLayerSet = mLayerSet;
}
else
{
renderLayerSet = mComposition->mapSettings().layers();
}
renderLayerSet = mLayerSet;
}
else
{
renderLayerSet = mComposition->mapSettings().layers();
}

QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerObject::MapLayers, exprVal, *evalContext ) )
{
renderLayerSet.clear();
Expand Down Expand Up @@ -595,16 +601,29 @@ QStringList QgsComposerMap::layersToRender( const QgsExpressionContext* context

QMap<QString, QString> QgsComposerMap::layerStyleOverridesToRender( const QgsExpressionContext& context ) const
{
QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, context ) )
if ( mFollowVisibilityPreset )
{
QString presetName = exprVal.toString();
QString presetName = mFollowVisibilityPresetName;

QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerObject::MapStylePreset, exprVal, context ) )
{
presetName = exprVal.toString();
}

if ( QgsProject::instance()->visibilityPresetCollection()->hasPreset( presetName ) )
return QgsProject::instance()->visibilityPresetCollection()->presetStyleOverrides( presetName );

else
return QMap<QString, QString>();
}
else if ( mKeepLayerStyles )
{
return mLayerStyleOverrides;
}
else
{
return QMap<QString, QString>();
}
return mLayerStyleOverrides;
}

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

// follow visibility preset
composerMapElem.setAttribute( "followPreset", mFollowVisibilityPreset ? "true" : "false" );
composerMapElem.setAttribute( "followPresetName", mFollowVisibilityPresetName );

//map rotation
composerMapElem.setAttribute( "mapRotation", QString::number( mMapRotation ) );

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

// follow visibility preset
mFollowVisibilityPreset = itemElem.attribute( "followPreset" ).compare( "true" ) == 0;
mFollowVisibilityPresetName = itemElem.attribute( "followPresetName" );

//mKeepLayerSet flag
QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
Expand Down
29 changes: 29 additions & 0 deletions src/core/composer/qgscomposermap.h
Expand Up @@ -262,6 +262,27 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
/** Stores the current layer styles into style overrides. @note added in 2.8 */
void storeCurrentLayerStyles();

/** Whether the map should follow a visibility preset. If true, the layers and layer styles
* will be used from given preset name (configured with setFollowVisibilityPresetName() method).
* This means when preset's settings are changed, the new settings are automatically
* picked up next time when rendering, without having to explicitly update them.
* At most one of the flags keepLayerSet() and followVisibilityPreset() should be enabled
* at any time since they are alternative approaches - if both are enabled,
* following visibility preset has higher priority. If neither is enabled (or if preset name is not set),
* map will use the same configuration as the map canvas uses.
* @note added in 2.16 */
bool followVisibilityPreset() const { return mFollowVisibilityPreset; }
/** Sets whether the map should follow a visibility preset. See followVisibilityPreset() for more details.
* @note added in 2.16 */
void setFollowVisibilityPreset( bool follow ) { mFollowVisibilityPreset = follow; }
/** Preset name that decides which layers and layer styles are used for map rendering. It is only
* used when followVisibilityPreset() returns true.
* @note added in 2.16 */
QString followVisibilityPresetName() const { return mFollowVisibilityPresetName; }
/** Sets preset name for map rendering. See followVisibilityPresetName() for more details.
* @note added in 2.16 */
void setFollowVisibilityPresetName( const QString& name ) { mFollowVisibilityPresetName = name; }

// Set cache outdated
void setCacheUpdated( bool u = false );

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

/** Whether layers and styles should be used from a preset (preset name is stored
* in mVisibilityPresetName and may be overridden by data-defined expression).
* This flag has higher priority than mKeepLayerSet. */
bool mFollowVisibilityPreset;
/** Visibility preset name to be used for map's layers and styles in case mFollowVisibilityPreset
* is true. May be overridden by data-defined expression. */
QString mFollowVisibilityPresetName;

/** Whether updates to the map are enabled */
bool mUpdatesEnabled;

Expand Down

0 comments on commit deee8e2

Please sign in to comment.