Skip to content

Commit 0749ba4

Browse files
author
Hugo Mercier
committed
Rework layer dependencies
A new class QgsMapLayerDependency allows to represent different kinds of dependencies between layers.
1 parent 1a5a7c5 commit 0749ba4

19 files changed

+243
-85
lines changed

ci/travis/linux/qt5/blacklist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ PyQgsSipCoverage
99
PyQgsSpatialiteProvider
1010
PyQgsVirtualLayerDefinition
1111
PyQgsVirtualLayerProvider
12+
PyQgsLayerDependencies
1213
qgis_composermapgridtest
1314
qgis_composerutils
1415
ProcessingGrass7AlgorithmsImageryTest

python/core/core.sip

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
%Include qgslogger.sip
8181
%Include qgsmaphittest.sip
8282
%Include qgsmaplayer.sip
83+
%Include qgsmaplayerdependency.sip
8384
%Include qgsmaplayerlegend.sip
8485
%Include qgsmaplayermodel.sip
8586
%Include qgsmaplayerproxymodel.sip

python/core/qgsmaplayer.sip

+15-6
Original file line numberDiff line numberDiff line change
@@ -678,20 +678,29 @@ class QgsMapLayer : QObject
678678

679679
/**
680680
* Sets the list of layers that may modify data/geometries of this layer when modified.
681-
* @see dataDependencies
681+
* @see dependencies()
682682
*
683683
* @param layersIds IDs of the layers that this layer depends on
684684
* @returns false if a dependency cycle has been detected (the change dependency set is not changed in that case)
685685
*/
686686
virtual bool setDataDependencies( const QSet<QString>& layersIds );
687687

688688
/**
689-
* Gets the list of layers that may modify data/geometries of this layer when modified.
690-
* @see setDataDependencies
689+
* Sets the list of layers that may modify data/geometries of this layer when modified.
690+
* @see dependencies()
691+
*
692+
* @param set of QgsMapLayerDependency. Only user-defined dependencies will be added
693+
* @returns false if a dependency cycle has been detected (the change dependency set is not changed in that case)
694+
*/
695+
bool setDataDependencies( const QSet<QgsMapLayerDependency>& layers );
696+
697+
/**
698+
* Gets the list of dependencies. This includes data dependencies set by the user (@see setDataDependencies)
699+
* as well as dependencies given by the provider
691700
*
692-
* @returns IDs of the layers that this layer depends on
701+
* @returns a set of QgsMapLayerDependency
693702
*/
694-
virtual QSet<QString> dataDependencies() const;
703+
virtual QSet<QgsMapLayerDependency> dependencies() const;
695704

696705
signals:
697706

@@ -785,5 +794,5 @@ class QgsMapLayer : QObject
785794
void setError( const QgsError &error );
786795

787796
//! Checks if new change dependency candidates introduce a cycle
788-
bool hasDataDependencyCycle( const QSet<QString>& layersIds ) const;
797+
bool hasDataDependencyCycle( const QSet<QgsMapLayerDependency>& layersIds ) const;
789798
};

python/core/qgsmaplayerdependency.sip

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
class QgsMapLayerDependency
2+
{
3+
%TypeHeaderCode
4+
#include "qgsmaplayerdependency.h"
5+
%End
6+
public:
7+
//! Type of dependency
8+
enum Type
9+
{
10+
PresenceDependency = 1, // The layer must be already present (in the registry) for this dependency to be resolved
11+
DataDependency = 2 // The layer may be invalidated by data changes on another layer
12+
};
13+
14+
//! Origin of the dependency
15+
enum Origin
16+
{
17+
FromProvider = 0, // Dependency given by the provider, the user cannot change it
18+
FromUser = 1 // Dependency given by the user
19+
};
20+
21+
//! Standard constructor
22+
QgsMapLayerDependency( QString layerId, Type type = DataDependency, Origin origin = FromUser );
23+
24+
//! Return the dependency type
25+
Type type() const;
26+
27+
//! Return the dependency origin
28+
Origin origin() const;
29+
30+
//! Return the ID of the layer this dependency depends on
31+
QString layerId() const;
32+
33+
bool operator==( const QgsMapLayerDependency& other ) const;
34+
};
35+
36+

python/core/qgsvectordataprovider.sip

+1-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ class QgsVectorDataProvider : QgsDataProvider
368368
/**
369369
* Get the list of layer ids on which this layer depends. This in particular determines the order of layer loading.
370370
*/
371-
virtual QSet<QString> layerDependencies() const;
371+
virtual QSet<QgsMapLayerDependency> dependencies() const;
372372

373373
signals:
374374
/** Signals an error in this provider */

python/core/qgsvectorlayer.sip

+4-10
Original file line numberDiff line numberDiff line change
@@ -421,12 +421,6 @@ class QgsVectorLayer : QgsMapLayer
421421

422422
const QList<QgsVectorJoinInfo> vectorJoins() const;
423423

424-
/**
425-
* Gets the list of layer ids on which this layer depends, as returned by the provider.
426-
* This in particular determines the order of layer loading.
427-
*/
428-
virtual QSet<QString> layerDependencies() const;
429-
430424
/**
431425
* Sets the list of layers that may modify data/geometries of this layer when modified.
432426
* This is meant mainly to declare database triggers between layers.
@@ -439,12 +433,12 @@ class QgsVectorLayer : QgsMapLayer
439433
bool setDataDependencies( const QSet<QString>& layersIds );
440434

441435
/**
442-
* Gets the list of layers that may modify data/geometries of this layer when modified.
443-
* @see setDataDependencies
436+
* Gets the list of dependencies. This includes data dependencies set by the user (@see setDataDependencies)
437+
* as well as dependencies given by the provider
444438
*
445-
* @returns IDs of the layers that this layer depends on
439+
* @returns a set of QgsMapLayerDependency
446440
*/
447-
QSet<QString> dataDependencies() const;
441+
virtual QSet<QgsMapLayerDependency> dependencies() const;
448442

449443
/**
450444
* Add a new field which is calculated by the expression specified

src/app/qgsvectorlayerproperties.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,11 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
302302

303303
mLayersDependenciesTreeGroup->setVisible( Qt::Unchecked );
304304

305-
QSet<QString> dependencySources = mLayer->dataDependencies();
305+
QSet<QString> dependencySources;
306+
Q_FOREACH ( const QgsMapLayerDependency& dep, mLayer->dependencies() )
307+
{
308+
dependencySources << dep.layerId();
309+
}
306310
Q_FOREACH ( QgsLayerTreeLayer* layer, mLayersDependenciesTreeGroup->findLayers() )
307311
{
308312
layer->setVisible( dependencySources.contains( layer->layerId() ) ? Qt::Checked : Qt::Unchecked );

src/core/qgsmaplayer.cpp

+30-14
Original file line numberDiff line numberDiff line change
@@ -1681,61 +1681,77 @@ void QgsMapLayer::setExtent( const QgsRectangle &r )
16811681
mExtent = r;
16821682
}
16831683

1684-
static QList<const QgsMapLayer*> _depOutEdges( const QgsMapLayer* vl, const QgsMapLayer* that, const QSet<QString>& layersIds )
1684+
static QList<const QgsMapLayer*> _depOutEdges( const QgsMapLayer* vl, const QgsMapLayer* that, const QSet<QgsMapLayerDependency>& layers )
16851685
{
16861686
QList<const QgsMapLayer*> lst;
16871687
if ( vl == that )
16881688
{
1689-
Q_FOREACH ( QString layerId, layersIds )
1689+
Q_FOREACH ( const QgsMapLayerDependency& dep, layers )
16901690
{
1691-
if ( const QgsMapLayer* l = QgsMapLayerRegistry::instance()->mapLayer( layerId ) )
1691+
if ( const QgsMapLayer* l = QgsMapLayerRegistry::instance()->mapLayer( dep.layerId() ) )
16921692
lst << l;
16931693
}
16941694
}
16951695
else
16961696
{
1697-
Q_FOREACH ( QString layerId, vl->dataDependencies() )
1697+
Q_FOREACH ( const QgsMapLayerDependency& dep, vl->dependencies() )
16981698
{
1699-
if ( const QgsMapLayer* l = QgsMapLayerRegistry::instance()->mapLayer( layerId ) )
1699+
if ( const QgsMapLayer* l = QgsMapLayerRegistry::instance()->mapLayer( dep.layerId() ) )
17001700
lst << l;
17011701
}
17021702
}
17031703
return lst;
17041704
}
17051705

1706-
static bool _depHasCycleDFS( const QgsMapLayer* n, QHash<const QgsMapLayer*, int>& mark, const QgsMapLayer* that, const QSet<QString>& layersIds )
1706+
static bool _depHasCycleDFS( const QgsMapLayer* n, QHash<const QgsMapLayer*, int>& mark, const QgsMapLayer* that, const QSet<QgsMapLayerDependency>& layers )
17071707
{
17081708
if ( mark.value( n ) == 1 ) // temporary
17091709
return true;
17101710
if ( mark.value( n ) == 0 ) // not visited
17111711
{
17121712
mark[n] = 1; // temporary
1713-
Q_FOREACH ( const QgsMapLayer* m, _depOutEdges( n, that, layersIds ) )
1713+
Q_FOREACH ( const QgsMapLayer* m, _depOutEdges( n, that, layers ) )
17141714
{
1715-
if ( _depHasCycleDFS( m, mark, that, layersIds ) )
1715+
if ( _depHasCycleDFS( m, mark, that, layers ) )
17161716
return true;
17171717
}
17181718
mark[n] = 2; // permanent
17191719
}
17201720
return false;
17211721
}
17221722

1723-
bool QgsMapLayer::hasDataDependencyCycle( const QSet<QString>& layersIds ) const
1723+
bool QgsMapLayer::hasDataDependencyCycle( const QSet<QgsMapLayerDependency>& layers ) const
17241724
{
17251725
QHash<const QgsMapLayer*, int> marks;
1726-
return _depHasCycleDFS( this, marks, this, layersIds );
1726+
return _depHasCycleDFS( this, marks, this, layers );
1727+
}
1728+
1729+
QSet<QgsMapLayerDependency> QgsMapLayer::dependencies() const
1730+
{
1731+
return mDataDependencies;
17271732
}
17281733

17291734
bool QgsMapLayer::setDataDependencies( const QSet<QString>& layersIds )
17301735
{
1731-
if ( hasDataDependencyCycle( layersIds ) )
1736+
QSet<QgsMapLayerDependency> deps;
1737+
Q_FOREACH ( QString layerId, layersIds )
1738+
{
1739+
deps << QgsMapLayerDependency( layerId );
1740+
}
1741+
if ( hasDataDependencyCycle( deps ) )
17321742
return false;
17331743

1734-
mDataDependencies = layersIds;
1744+
mDataDependencies = deps;
17351745
return true;
17361746
}
17371747

1738-
QSet<QString> QgsMapLayer::dataDependencies() const
1748+
bool QgsMapLayer::setDataDependencies( const QSet<QgsMapLayerDependency>& layers )
17391749
{
1740-
return mDataDependencies;
1750+
QSet<QString> deps;
1751+
Q_FOREACH ( const QgsMapLayerDependency& dep, layers )
1752+
{
1753+
if ( dep.origin() == QgsMapLayerDependency::FromUser && dep.type() == QgsMapLayerDependency::DataDependency )
1754+
deps << dep.layerId();
1755+
}
1756+
return setDataDependencies( deps );
17411757
}

src/core/qgsmaplayer.h

+17-7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "qgsrectangle.h"
3333
#include "qgscoordinatereferencesystem.h"
3434
#include "qgsrendercontext.h"
35+
#include "qgsmaplayerdependency.h"
3536

3637
class QgsMapLayerLegend;
3738
class QgsMapLayerRenderer;
@@ -700,20 +701,29 @@ class CORE_EXPORT QgsMapLayer : public QObject
700701

701702
/**
702703
* Sets the list of layers that may modify data/geometries of this layer when modified.
703-
* @see dataDependencies
704+
* @see dependencies()
704705
*
705706
* @param layersIds IDs of the layers that this layer depends on
706707
* @returns false if a dependency cycle has been detected (the change dependency set is not changed in that case)
707708
*/
708709
virtual bool setDataDependencies( const QSet<QString>& layersIds );
709710

710711
/**
711-
* Gets the list of layers that may modify data/geometries of this layer when modified.
712-
* @see setDataDependencies
712+
* Sets the list of layers that may modify data/geometries of this layer when modified.
713+
* @see dependencies()
714+
*
715+
* @param layers set of QgsMapLayerDependency. Only user-defined dependencies will be added
716+
* @returns false if a dependency cycle has been detected (the change dependency set is not changed in that case)
717+
*/
718+
bool setDataDependencies( const QSet<QgsMapLayerDependency>& layers );
719+
720+
/**
721+
* Gets the list of dependencies. This includes data dependencies set by the user (@see setDataDependencies)
722+
* as well as dependencies given by the provider
713723
*
714-
* @returns IDs of the layers that this layer depends on
724+
* @returns a set of QgsMapLayerDependency
715725
*/
716-
virtual QSet<QString> dataDependencies() const;
726+
virtual QSet<QgsMapLayerDependency> dependencies() const;
717727

718728
signals:
719729

@@ -854,10 +864,10 @@ class CORE_EXPORT QgsMapLayer : public QObject
854864
QgsError mError;
855865

856866
//! List of layers that may modify this layer on modification
857-
QSet<QString> mDataDependencies;
867+
QSet<QgsMapLayerDependency> mDataDependencies;
858868

859869
//! Checks whether a new set of data dependencies will introduce a cycle
860-
bool hasDataDependencyCycle( const QSet<QString>& layersIds ) const;
870+
bool hasDataDependencyCycle( const QSet<QgsMapLayerDependency>& layers ) const;
861871

862872
private:
863873
/**

src/core/qgsmaplayerdependency.h

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
2+
/***************************************************************************
3+
qgsmaplayerdependency.h - description
4+
-------------------
5+
begin : September 2016
6+
copyright : (C) 2016 by Hugo Mercier
7+
email : hugo dot mercier at oslandia dot com
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
19+
#ifndef QGSMAPLAYERDEPENDENCY_H
20+
#define QGSMAPLAYERDEPENDENCY_H
21+
22+
#include <QString>
23+
24+
/** \ingroup core
25+
* This class models dependencies with or between map layers.
26+
* A dependency is defined by a layer ID, a type and an origin.
27+
* The two combinations of type/origin that are currently supported are:
28+
* - PresenceDependency && FromProvider: virtual layers for instance which may depend on other layers already loaded to work
29+
* - DataDependency && FromUser: dependencies given by the user, mainly to represent database triggers
30+
*
31+
* @note added in QGIS 3.0
32+
*/
33+
class CORE_EXPORT QgsMapLayerDependency
34+
{
35+
public:
36+
//! Type of dependency
37+
enum Type
38+
{
39+
PresenceDependency = 1, //< The layer must be already present (in the registry) for this dependency to be resolved
40+
DataDependency = 2 //< The layer may be invalidated by data changes on another layer
41+
};
42+
43+
//! Origin of the dependency
44+
enum Origin
45+
{
46+
FromProvider = 0, //< Dependency given by the provider, the user cannot change it
47+
FromUser = 1 //< Dependency given by the user
48+
};
49+
50+
//! Standard constructor
51+
QgsMapLayerDependency( QString layerId, Type type = DataDependency, Origin origin = FromUser ) :
52+
mType( type ),
53+
mOrigin( origin ),
54+
mLayerId( layerId )
55+
{}
56+
57+
//! Return the dependency type
58+
Type type() const { return mType; }
59+
60+
//! Return the dependency origin
61+
Origin origin() const { return mOrigin; }
62+
63+
//! Return the ID of the layer this dependency depends on
64+
QString layerId() const { return mLayerId; }
65+
66+
//! Comparison operator
67+
bool operator==( const QgsMapLayerDependency& other ) const
68+
{
69+
return layerId() == other.layerId() && origin() == other.origin() && type() == other.type();
70+
}
71+
private:
72+
Type mType;
73+
Origin mOrigin;
74+
QString mLayerId;
75+
};
76+
77+
/**
78+
* global qHash function for QgsMapLayerDependency, so that it can be used in a QSet
79+
*/
80+
inline uint qHash( const QgsMapLayerDependency& dep )
81+
{
82+
return qHash( dep.layerId() ) + dep.origin() + dep.type();
83+
}
84+
85+
#endif

src/core/qgsproject.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ bool QgsProject::read()
893893
QMap<QString, QgsMapLayer*> existingMaps = QgsMapLayerRegistry::instance()->mapLayers();
894894
for ( QMap<QString, QgsMapLayer*>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
895895
{
896-
it.value()->setDataDependencies( it.value()->dataDependencies() );
896+
it.value()->setDataDependencies( it.value()->dependencies() );
897897
}
898898

899899
// read the project: used by map canvas and legend
@@ -997,7 +997,7 @@ void QgsProject::onMapLayersAdded( const QList<QgsMapLayer*>& layers )
997997
// check if we have to update connections for layers with dependencies
998998
for ( QMap<QString, QgsMapLayer*>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
999999
{
1000-
QSet<QString> deps = it.value()->dataDependencies();
1000+
QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
10011001
if ( deps.contains( layer->id() ) )
10021002
{
10031003
// reconnect to change signals

0 commit comments

Comments
 (0)