[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.
This commit is contained in:
Martin Dobias 2016-05-11 19:15:20 +08:00
parent 8c402bcf42
commit deee8e290e
6 changed files with 256 additions and 102 deletions

View File

@ -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 );

View File

@ -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 )
{
@ -301,8 +308,11 @@ 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;
@ -310,19 +320,35 @@ void QgsComposerMapWidget::aboutToShowVisibilityPresetsMenu()
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 )
@ -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 )
@ -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() )
{
@ -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 )
@ -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
{

View File

@ -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 );
@ -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;

View File

@ -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 )
@ -98,6 +99,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
, mEvaluatedMapRotation( 0 )
, mKeepLayerSet( false )
, mKeepLayerStyles( false )
, mFollowVisibilityPreset( false )
, mUpdatesEnabled( true )
, mMapCanvas( nullptr )
, mDrawCanvasItems( true )
@ -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 );
}
//use stored layer set or read current set from main canvas
if ( renderLayerSet.isEmpty() )
{
if ( mKeepLayerSet )
{
renderLayerSet = mLayerSet;
}
else
{
else // fallback to using map canvas layers
renderLayerSet = mComposition->mapSettings().layers();
}
}
else if ( mKeepLayerSet )
{
renderLayerSet = mLayerSet;
}
else
{
renderLayerSet = mComposition->mapSettings().layers();
}
QVariant exprVal;
if ( dataDefinedEvaluate( QgsComposerObject::MapLayers, exprVal, *evalContext ) )
{
renderLayerSet.clear();
@ -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
@ -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 ) );
@ -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 )

View File

@ -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 );
@ -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;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>457</width>
<height>1086</height>
<width>465</width>
<height>2617</height>
</rect>
</property>
<property name="sizePolicy">
@ -23,16 +23,7 @@
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
@ -63,9 +54,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-1044</y>
<width>438</width>
<height>2111</height>
<y>0</y>
<width>447</width>
<height>2584</height>
</rect>
</property>
<property name="sizePolicy">
@ -90,7 +81,7 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0" colspan="3">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="mPreviewModeComboBox">
@ -111,13 +102,6 @@
</item>
</layout>
</item>
<item row="4" column="0" colspan="3">
<widget class="QCheckBox" name="mDrawCanvasItemsCheckBox">
<property name="text">
<string>Draw map canvas items</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
@ -128,7 +112,7 @@
</property>
</widget>
</item>
<item row="1" column="2">
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="mScaleLineEdit">
@ -146,7 +130,63 @@
</item>
</layout>
</item>
<item row="5" column="0" colspan="3">
<item row="2" column="0">
<widget class="QLabel" name="mMapRotationLabel">
<property name="text">
<string>Map rotation</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QgsDoubleSpinBox" name="mMapRotationSpinBox">
<property name="suffix">
<string> °</string>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mMapRotationDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="mDrawCanvasItemsCheckBox">
<property name="text">
<string>Draw map canvas items</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QCheckBox" name="mFollowVisibilityPresetCheckBox">
<property name="text">
<string>Follow visibility preset</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="mFollowVisibilityPresetCombo"/>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mStylePresetsDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QCheckBox" name="mKeepLayerListCheckBox">
@ -193,52 +233,13 @@
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mMapRotationLabel">
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="mKeepLayerStylesCheckBox">
<property name="text">
<string>Map rotation</string>
<string>Lock layer styles for map item</string>
</property>
</widget>
</item>
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QgsDoubleSpinBox" name="mMapRotationSpinBox">
<property name="suffix">
<string> °</string>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mMapRotationDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QCheckBox" name="mKeepLayerStylesCheckBox">
<property name="text">
<string>Lock layer styles for map item</string>
</property>
</widget>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mStylePresetsDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -1461,11 +1462,13 @@
<tabstop>mMapRotationSpinBox</tabstop>
<tabstop>mMapRotationDDBtn</tabstop>
<tabstop>mDrawCanvasItemsCheckBox</tabstop>
<tabstop>mFollowVisibilityPresetCheckBox</tabstop>
<tabstop>mFollowVisibilityPresetCombo</tabstop>
<tabstop>mStylePresetsDDBtn</tabstop>
<tabstop>mKeepLayerListCheckBox</tabstop>
<tabstop>mLayerListFromPresetButton</tabstop>
<tabstop>mLayersDDBtn</tabstop>
<tabstop>mKeepLayerStylesCheckBox</tabstop>
<tabstop>mStylePresetsDDBtn</tabstop>
<tabstop>mExtentsCheckBox</tabstop>
<tabstop>mXMinLineEdit</tabstop>
<tabstop>mXMinDDBtn</tabstop>