Skip to content

Commit

Permalink
Migrate QgsMapThemeCollection away from using layer IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Dec 10, 2016
1 parent c143be7 commit 5fc10d6
Show file tree
Hide file tree
Showing 22 changed files with 466 additions and 426 deletions.
3 changes: 3 additions & 0 deletions python/core/layertree/qgslayertreegroup.sip
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class QgsLayerTreeGroup : QgsLayerTreeNode
//! Remove all child nodes. The nodes will be deleted.
void removeAllChildren();

//! Find layer node representing the map layer. Searches recursively the whole sub-tree.
//! @note added in 3.0
QgsLayerTreeLayer* findLayer( QgsMapLayer* layer ) const;
//! Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tree.
QgsLayerTreeLayer* findLayer( const QString& layerId );
//! Find all layer nodes. Searches recursively the whole sub-tree.
Expand Down
97 changes: 54 additions & 43 deletions python/core/qgsmapthemecollection.sip
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,33 @@ class QgsMapThemeCollection : QObject
%End
public:

/**
* \ingroup core
* Individual record of a visible layer in a map theme record.
* @note Added in QGIS 3.0
*/
class MapThemeLayerRecord
{
public:
//! Initialize layer record with a map layer - it will be stored as a weak pointer
MapThemeLayerRecord( QgsMapLayer* l = nullptr );

bool operator==( const QgsMapThemeCollection::MapThemeLayerRecord& other ) const;
bool operator!=( const QgsMapThemeCollection::MapThemeLayerRecord& other ) const;

//! Returns map layer or null if the layer does not exist anymore
QgsMapLayer* layer() const;

//! Whether current style is valid and should be applied
bool usingCurrentStyle;
//! Name of the current style of the layer
QString currentStyle;
//! Whether checkedLegendItems should be applied
bool usingLegendItems;
//! Rule keys of check legend items in layer tree model
QSet<QString> checkedLegendItems;
};

/** \ingroup core
* Individual map theme record of visible layers and styles.
*/
Expand All @@ -23,42 +50,15 @@ class QgsMapThemeCollection : QObject
bool operator==( const QgsMapThemeCollection::MapThemeRecord& other ) const;
bool operator!=( const QgsMapThemeCollection::MapThemeRecord& other ) const;

/**
* Ordered list of visible layers
*/
QStringList visibleLayerIds() const;

/**
* Ordered list of visible layers
*/
void setVisibleLayerIds( const QStringList& visibleLayerIds );

/**
* Lists which legend symbols are checked for layers which support this and where
* not all symbols are checked.
* @note not available in Python bindings
*/
// QMap<QString, QSet<QString> > perLayerCheckedLegendSymbols() const;

/**
* Lists which legend symbols are checked for layers which support this and where
* not all symbols are checked.
* @note not available in Python bindings
*/
// void setPerLayerCheckedLegendSymbols(const QMap<QString, QSet<QString> >& perLayerCheckedLegendSymbols);

/**
* The currently used style name for layers with multiple styles.
* The map has layer ids as keys and style names as values.
*/
QMap<QString, QString> perLayerCurrentStyle() const;

/**
* The currently used style name for layers with multiple styles.
* The map has layer ids as keys and style names as values.
*/
void setPerLayerCurrentStyle(const QMap<QString, QString>& perLayerCurrentStyle);
//! Returns a list of records for all visible layer belonging to the theme.
QList<QgsMapThemeCollection::MapThemeLayerRecord> layerRecords() const;

//! Sets layer records for the theme.
void setLayerRecords( const QList<QgsMapThemeCollection::MapThemeLayerRecord>& records );

//! Return set with only records for valid layers
//! @note not available in python bindings
// QHash<QgsMapLayer*, QgsMapThemeCollection::MapThemeLayerRecord> validLayerRecords() const;
};

/**
Expand Down Expand Up @@ -106,13 +106,18 @@ class QgsMapThemeCollection : QObject
*
* @note The order of the returned list is not guaranteed to reflect the order of layers
* in the canvas.
* @note Added in QGIS 3.0
*/
QStringList mapThemeVisibleLayers( const QString& name ) const;
QStringList mapThemeVisibleLayerIds( const QString& name ) const;

/**
* Apply check states of legend nodes of a given layer as defined in the map theme.
* Returns the list of layers that are visible for the specified map theme.
*
* @note The order of the returned list is not guaranteed to reflect the order of layers
* in the canvas.
* @note Added in QGIS 3.0
*/
void applyMapThemeCheckedLegendNodesToLayer( const QString& name, const QString& layerID );
QList<QgsMapLayer*> mapThemeVisibleLayers( const QString& name ) const;

/**
* Get layer style overrides (for QgsMapSettings) of the visible layers for given map theme.
Expand All @@ -133,12 +138,18 @@ class QgsMapThemeCollection : QObject
void writeXml( QDomDocument& doc );

/**
* Static method for adding visible layers from a layer tree group to a map theme
* record.
* @param parent layer tree group parent
* @param rec map theme record to amend
* Static method to create theme from the current state of layer visibilities in layer tree,
* current style of layers and check state of legend items (from a layer tree model).
* @note added in QGIS 3.0
*/
static MapThemeRecord createThemeFromCurrentState( QgsLayerTreeGroup* root, QgsLayerTreeModel* model );

/**
* Apply theme given by its name and modify layer tree, current style of layers and checked
* legend items of passed layer tree model.
* @note added in QGIS 3.0
*/
static void addVisibleLayersToMapTheme( QgsLayerTreeGroup* parent, MapThemeRecord& rec );
void applyTheme( const QString& name, QgsLayerTreeGroup* root, QgsLayerTreeModel* model );

signals:

Expand Down
2 changes: 1 addition & 1 deletion src/app/composer/qgscomposermapwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void QgsComposerMapWidget::keepLayersVisibilityPresetSelected()
return;

QString presetName = action->text();
QList<QgsMapLayer*> lst = QgsMapThemes::instance()->orderedPresetVisibleLayers2( presetName );
QList<QgsMapLayer*> lst = QgsMapThemes::instance()->orderedPresetVisibleLayers( presetName );
if ( mComposerMap )
{
mKeepLayerListCheckBox->setChecked( true );
Expand Down
4 changes: 2 additions & 2 deletions src/app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1199,9 +1199,9 @@ int main( int argc, char *argv[] )
QList< QPair<QgsVectorLayer *, int > > layers;
if ( !dxfPreset.isEmpty() )
{
Q_FOREACH ( const QString& layer, QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( dxfPreset ) )
Q_FOREACH ( QgsMapLayer* layer, QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( dxfPreset ) )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layer ) );
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
if ( !vl )
continue;
layers << qMakePair<QgsVectorLayer *, int>( vl, -1 );
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsdxfexportdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ void QgsVectorLayerAndAttributeModel::applyVisibilityPreset( const QString &name
}
else
{
visibleLayers = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( name ).toSet();
visibleLayers = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayerIds( name ).toSet();
}

if ( visibleLayers.isEmpty() )
Expand Down
136 changes: 12 additions & 124 deletions src/app/qgsmapthemes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,66 +54,12 @@ QgsMapThemes::QgsMapThemes()
connect( mMenu, SIGNAL( aboutToShow() ), this, SLOT( menuAboutToShow() ) );
}

void QgsMapThemes::addPerLayerCheckedLegendSymbols( QgsMapThemeCollection::MapThemeRecord& rec )
{
QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();

Q_FOREACH ( const QString& layerID, rec.visibleLayerIds() )
{
QgsLayerTreeLayer* nodeLayer = model->rootGroup()->findLayer( layerID );
if ( !nodeLayer )
continue;

bool hasCheckableItems = false;
bool someItemsUnchecked = false;
QSet<QString> checkedItems;
Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, model->layerLegendNodes( nodeLayer, true ) )
{
if ( legendNode->flags() & Qt::ItemIsUserCheckable )
{
hasCheckableItems = true;

if ( legendNode->data( Qt::CheckStateRole ).toInt() == Qt::Checked )
checkedItems << legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
else
someItemsUnchecked = true;
}
}

QMap<QString, QSet<QString> > checkedSymbols = rec.perLayerCheckedLegendSymbols();

if ( hasCheckableItems && someItemsUnchecked )
checkedSymbols.insert( nodeLayer->layerId(), checkedItems );

rec.setPerLayerCheckedLegendSymbols( checkedSymbols );
}
}

void QgsMapThemes::addPerLayerCurrentStyle( QgsMapThemeCollection::MapThemeRecord& rec )
{
QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();

QMap<QString, QString> styles = rec.perLayerCurrentStyle();

Q_FOREACH ( const QString& layerID, rec.visibleLayerIds() )
{
QgsLayerTreeLayer* nodeLayer = model->rootGroup()->findLayer( layerID );
if ( !nodeLayer )
continue;

styles[layerID] = nodeLayer->layer()->styleManager()->currentStyle();
}
rec.setPerLayerCurrentStyle( styles );
}

QgsMapThemeCollection::MapThemeRecord QgsMapThemes::currentState()
{
QgsMapThemeCollection::MapThemeRecord rec;
QgsLayerTreeGroup* root = QgsProject::instance()->layerTreeRoot();
QgsMapThemeCollection::addVisibleLayersToMapTheme( root, rec );
addPerLayerCheckedLegendSymbols( rec );
addPerLayerCurrentStyle( rec );
return rec;
QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();
return QgsMapThemeCollection::createThemeFromCurrentState( root, model );
}

QgsMapThemes* QgsMapThemes::instance()
Expand All @@ -134,30 +80,21 @@ void QgsMapThemes::updatePreset( const QString& name )
QgsProject::instance()->mapThemeCollection()->update( name, currentState() );
}

QStringList QgsMapThemes::orderedPresetVisibleLayers( const QString& name ) const
QList<QgsMapLayer*> QgsMapThemes::orderedPresetVisibleLayers( const QString& name ) const
{
QStringList visibleIds = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( name );
QStringList visibleIds = QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayerIds( name );

// also make sure to order the layers according to map canvas order
QgsLayerTreeMapCanvasBridge* bridge = QgisApp::instance()->layerTreeCanvasBridge();
QStringList order = bridge->hasCustomLayerOrder() ? bridge->customLayerOrder() : bridge->defaultLayerOrder();
QStringList order2;
QList<QgsMapLayer*> lst;
Q_FOREACH ( const QString& layerID, order )
{
if ( visibleIds.contains( layerID ) )
order2 << layerID;
}

return order2;
}

QList<QgsMapLayer*> QgsMapThemes::orderedPresetVisibleLayers2( const QString& name ) const
{
QList<QgsMapLayer*> lst;
Q_FOREACH ( const QString& layerId, orderedPresetVisibleLayers( name ) )
{
if ( QgsMapLayer* layer = QgsProject::instance()->mapLayer( layerId ) )
lst << layer;
{
if ( QgsMapLayer* layer = QgsProject::instance()->mapLayer( layerID ) )
lst << layer;
}
}
return lst;
}
Expand Down Expand Up @@ -202,64 +139,15 @@ void QgsMapThemes::replaceTriggered()
addPreset( actionPreset->text() );
}

void QgsMapThemes::applyStateToLayerTreeGroup( QgsLayerTreeGroup* parent, const QgsMapThemeCollection::MapThemeRecord& rec )
{
Q_FOREACH ( QgsLayerTreeNode* node, parent->children() )
{
if ( QgsLayerTree::isGroup( node ) )
applyStateToLayerTreeGroup( QgsLayerTree::toGroup( node ), rec );
else if ( QgsLayerTree::isLayer( node ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
bool isVisible = rec.visibleLayerIds().contains( nodeLayer->layerId() );
nodeLayer->setVisible( isVisible ? Qt::Checked : Qt::Unchecked );

if ( isVisible )
{
if ( rec.perLayerCurrentStyle().contains( nodeLayer->layerId() ) )
{
// apply desired style first
nodeLayer->layer()->styleManager()->setCurrentStyle( rec.perLayerCurrentStyle().value( nodeLayer->layerId() ) );
}

QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();
if ( rec.perLayerCheckedLegendSymbols().contains( nodeLayer->layerId() ) )
{
const QSet<QString>& checkedNodes = rec.perLayerCheckedLegendSymbols().value( nodeLayer->layerId() );
// some nodes are not checked
Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, model->layerLegendNodes( nodeLayer, true ) )
{
Qt::CheckState shouldHaveState = checkedNodes.contains( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() ) ? Qt::Checked : Qt::Unchecked;
if (( legendNode->flags() & Qt::ItemIsUserCheckable ) &&
legendNode->data( Qt::CheckStateRole ).toInt() != shouldHaveState )
legendNode->setData( shouldHaveState, Qt::CheckStateRole );
}
}
else
{
// all nodes should be checked
Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, model->layerLegendNodes( nodeLayer, true ) )
{
if (( legendNode->flags() & Qt::ItemIsUserCheckable ) &&
legendNode->data( Qt::CheckStateRole ).toInt() != Qt::Checked )
legendNode->setData( Qt::Checked, Qt::CheckStateRole );
}
}
}
}
}
}


void QgsMapThemes::applyState( const QString& presetName )
{
if ( !QgsProject::instance()->mapThemeCollection()->hasMapTheme( presetName ) )
return;

applyStateToLayerTreeGroup( QgsProject::instance()->layerTreeRoot(), QgsProject::instance()->mapThemeCollection()->mapThemeState( presetName ) );

// also make sure that the preset is up-to-date (not containing any non-existent legend items)
QgsProject::instance()->mapThemeCollection()->update( presetName, currentState() );
QgsLayerTreeGroup* root = QgsProject::instance()->layerTreeRoot();
QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();
QgsProject::instance()->mapThemeCollection()->applyTheme( presetName, root, model );
}

void QgsMapThemes::removeCurrentPreset()
Expand Down
6 changes: 1 addition & 5 deletions src/app/qgsmapthemes.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,7 @@ class APP_EXPORT QgsMapThemes : public QObject

//! Return list of layer IDs that should be visible for particular preset.
//! The order will match the layer order from the map canvas
QStringList orderedPresetVisibleLayers( const QString& name ) const;

//! Return list of layer IDs that should be visible for particular preset.
//! The order will match the layer order from the map canvas
QList<QgsMapLayer*> orderedPresetVisibleLayers2( const QString& name ) const;
QList<QgsMapLayer*> orderedPresetVisibleLayers( const QString& name ) const;

//! Convenience menu that lists available presets and actions for management
QMenu* menu();
Expand Down
12 changes: 2 additions & 10 deletions src/core/composer/qgsatlascomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -628,16 +628,8 @@ void QgsAtlasComposition::readXml( const QDomElement& atlasElem, const QDomDocum
}

// look for stored layer name
mCoverageLayer = nullptr;
QMap<QString, QgsMapLayer*> layers = QgsProject::instance()->mapLayers();
for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
{
if ( it.key() == atlasElem.attribute( QStringLiteral( "coverageLayer" ) ) )
{
mCoverageLayer = dynamic_cast<QgsVectorLayer*>( it.value() );
break;
}
}
QString coverageLayerId = atlasElem.attribute( QStringLiteral( "coverageLayer" ) );
mCoverageLayer = qobject_cast<QgsVectorLayer*>( QgsProject::instance()->mapLayer( coverageLayerId ) );

mPageNameExpression = atlasElem.attribute( QStringLiteral( "pageNameExpression" ), QString() );
mSingleFile = atlasElem.attribute( QStringLiteral( "singleFile" ), QStringLiteral( "false" ) ) == QLatin1String( "true" ) ? true : false;
Expand Down
Loading

0 comments on commit 5fc10d6

Please sign in to comment.