Skip to content
Permalink
Browse files

Merge pull request #3320 from mhugo/fix_snapping2

Data dependency between layers + snapping fix
  • Loading branch information
Hugo Mercier
Hugo Mercier committed Aug 31, 2016
2 parents 504badb + 0749ba4 commit bd3cf76f5fec677d01919e30af875a54dc06dc2b
@@ -9,6 +9,7 @@ PyQgsSipCoverage
PyQgsSpatialiteProvider
PyQgsVirtualLayerDefinition
PyQgsVirtualLayerProvider
PyQgsLayerDependencies

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Aug 31, 2016

Member

Not sure we should add new tests to the blacklist at this stage of the transition towards Qt5

This comment has been minimized.

Copy link
@3nids

3nids Aug 31, 2016

Member

This seems to be due to the spatialite problem. So not really strictl a new item to the blacklist.
What's the status for spatialite?

This comment has been minimized.

Copy link
@mhugo

mhugo Aug 31, 2016

Yes, this is the pyspatialite problem.
For what I understood from #2701 using standard sqlite3 module + "load_extension(mod_spatialite)" is the good way to do, assuming we will have "mod_spatialite" for windows and mac ...

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Aug 31, 2016

Member

It has been obsoleted by geopackage.

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Aug 31, 2016

Member

For what I understood from #2701 using standard sqlite3 module + "load_extension(mod_spatialite)" is the good way to do, assuming we will have "mod_spatialite" for windows and mac ...

+1 for this approach. It should work on travis so should be ok to avoid regressions and hopefully other OS's just follow.

This comment has been minimized.

Copy link
@mhugo

mhugo Aug 31, 2016

Ok, then I will try to propose a fix soon (that will be 4 items less in the blacklist).

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Aug 31, 2016

Member

Well, that is good news :)

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Aug 31, 2016

Member

A "qgis.utils.connect_spatialite" method that goes to spatialite or sqlite library depending on availability would be nice ;)

This comment has been minimized.

Copy link
@mhugo

mhugo Aug 31, 2016

Is it possible to add triggers to a geopackage ? With GDAL ?

This comment has been minimized.

Copy link
@mhugo

mhugo Aug 31, 2016

Yeah I was thinking about something like that: qgis.utils.connect_spatialite or qgis.pyspatialite

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Aug 31, 2016

Member

Is it possible to add triggers to a geopackage ? With GDAL ?

No idea :)

qgis_composermapgridtest
qgis_composerutils
ProcessingGrass7AlgorithmsImageryTest
@@ -665,6 +665,7 @@
<file>themes/default/mActionAddAfsLayer.svg</file>
<file>themes/default/mIconFormSelect.svg</file>
<file>themes/default/mActionMultiEdit.svg</file>
<file>themes/default/dependencies.svg</file>
</qresource>
<qresource prefix="/images/tips">
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="sync_views.svg">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient4158">
<stop
style="stop-color:#0000ff;stop-opacity:1"
offset="0"
id="stop4160" />
<stop
style="stop-color:#0000a9;stop-opacity:1"
offset="1"
id="stop4162" />
</linearGradient>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;"
inkscape:isstock="true">
<path
id="path4171"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Mend-8"
style="overflow:visible"
inkscape:isstock="true">
<path
inkscape:connector-curvature="0"
id="path4171-2"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="scale(-0.6,-0.6)" />
</marker>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4158"
id="radialGradient4178"
cx="7.9999766"
cy="1040.8622"
fx="7.9999766"
fy="1040.8622"
r="6.9999766"
gradientTransform="matrix(1,0,0,0.49999621,0,520.43504)"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4158"
id="radialGradient4178-3"
cx="7.9999766"
cy="1040.8622"
fx="7.9999766"
fy="1040.8622"
r="6.9999766"
gradientTransform="matrix(-1,0,0,0.49999621,16,527.43502)"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="18.5"
inkscape:cx="0.59568033"
inkscape:cy="10.721345"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:window-width="1600"
inkscape:window-height="829"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid4136" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<g
id="g4865">
<path
style="fill:url(#radialGradient4178);fill-opacity:1;fill-rule:evenodd;stroke:#0000a9;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.5,1040.8622 0,1 8,0 0,2 5,-3 -5,-3 0,2 -8,0 z"
id="path4156"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:url(#radialGradient4178-3);fill-opacity:1;fill-rule:evenodd;stroke:#0000a9;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 14.5,1047.8622 0,1 -7.9999996,0 0,2 -5,-3 5,-3 0,2 7.9999996,0 z"
id="path4156-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
</g>
</g>
</svg>
@@ -80,6 +80,7 @@
%Include qgslogger.sip
%Include qgsmaphittest.sip
%Include qgsmaplayer.sip
%Include qgsmaplayerdependency.sip
%Include qgsmaplayerlegend.sip
%Include qgsmaplayermodel.sip
%Include qgsmaplayerproxymodel.sip
@@ -676,6 +676,32 @@ class QgsMapLayer : QObject
*/
void emitStyleChanged();

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

/**
* Sets the list of layers that may modify data/geometries of this layer when modified.
* @see dependencies()
*
* @param set of QgsMapLayerDependency. Only user-defined dependencies will be added
* @returns false if a dependency cycle has been detected (the change dependency set is not changed in that case)
*/
bool setDataDependencies( const QSet<QgsMapLayerDependency>& layers );

/**
* Gets the list of dependencies. This includes data dependencies set by the user (@see setDataDependencies)
* as well as dependencies given by the provider
*
* @returns a set of QgsMapLayerDependency
*/
virtual QSet<QgsMapLayerDependency> dependencies() const;

signals:

/** Emit a signal with status (e.g. to be caught by QgisApp and display a msg on status bar) */
@@ -766,4 +792,7 @@ class QgsMapLayer : QObject
void appendError( const QgsErrorMessage &error );
/** Set error message */
void setError( const QgsError &error );

//! Checks if new change dependency candidates introduce a cycle
bool hasDataDependencyCycle( const QSet<QgsMapLayerDependency>& layersIds ) const;
};
@@ -0,0 +1,36 @@
class QgsMapLayerDependency
{
%TypeHeaderCode
#include "qgsmaplayerdependency.h"
%End
public:
//! Type of dependency
enum Type
{
PresenceDependency = 1, // The layer must be already present (in the registry) for this dependency to be resolved
DataDependency = 2 // The layer may be invalidated by data changes on another layer
};

//! Origin of the dependency
enum Origin
{
FromProvider = 0, // Dependency given by the provider, the user cannot change it
FromUser = 1 // Dependency given by the user
};

//! Standard constructor
QgsMapLayerDependency( QString layerId, Type type = DataDependency, Origin origin = FromUser );

//! Return the dependency type
Type type() const;

//! Return the dependency origin
Origin origin() const;

//! Return the ID of the layer this dependency depends on
QString layerId() const;

bool operator==( const QgsMapLayerDependency& other ) const;
};


@@ -368,7 +368,7 @@ class QgsVectorDataProvider : QgsDataProvider
/**
* Get the list of layer ids on which this layer depends. This in particular determines the order of layer loading.
*/
virtual QSet<QString> layerDependencies() const;
virtual QSet<QgsMapLayerDependency> dependencies() const;

signals:
/** Signals an error in this provider */
@@ -422,9 +422,23 @@ class QgsVectorLayer : QgsMapLayer
const QList<QgsVectorJoinInfo> vectorJoins() const;

/**
* Get the list of layer ids on which this layer depends. This in particular determines the order of layer loading.
* Sets the list of layers that may modify data/geometries of this layer when modified.
* This is meant mainly to declare database triggers between layers.
* When one of these layers is modified (feature added/deleted or geometry changed),
* dataChanged() will be emitted, allowing users of this layer to refresh / update it.
*
* @param layersIds IDs of the layers that this layer depends on
* @returns false if a dependency cycle has been detected (the change dependency set is not changed in that case)
*/
bool setDataDependencies( const QSet<QString>& layersIds );

/**
* Gets the list of dependencies. This includes data dependencies set by the user (@see setDataDependencies)
* as well as dependencies given by the provider
*
* @returns a set of QgsMapLayerDependency
*/
virtual QSet<QString> layerDependencies() const;
virtual QSet<QgsMapLayerDependency> dependencies() const;

/**
* Add a new field which is calculated by the expression specified
@@ -52,6 +52,7 @@
#include "qgsdatasourceuri.h"
#include "qgsrenderer.h"
#include "qgsexpressioncontext.h"
#include "layertree/qgslayertreelayer.h"

#include <QMessageBox>
#include <QDir>
@@ -291,6 +292,27 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(

QString title = QString( tr( "Layer Properties - %1" ) ).arg( mLayer->name() );
restoreOptionsBaseUi( title );

mLayersDependenciesTreeGroup.reset( QgsProject::instance()->layerTreeRoot()->clone() );
QgsLayerTreeLayer* layer = mLayersDependenciesTreeGroup->findLayer( mLayer->id() );
layer->parent()->takeChild( layer );
mLayersDependenciesTreeModel.reset( new QgsLayerTreeModel( mLayersDependenciesTreeGroup.data() ) );
// use visibility as selection
mLayersDependenciesTreeModel->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );

mLayersDependenciesTreeGroup->setVisible( Qt::Unchecked );

QSet<QString> dependencySources;
Q_FOREACH ( const QgsMapLayerDependency& dep, mLayer->dependencies() )
{
dependencySources << dep.layerId();
}
Q_FOREACH ( QgsLayerTreeLayer* layer, mLayersDependenciesTreeGroup->findLayers() )
{
layer->setVisible( dependencySources.contains( layer->layerId() ) ? Qt::Checked : Qt::Unchecked );
}

mLayersDependenciesTreeView->setModel( mLayersDependenciesTreeModel.data() );
} // QgsVectorLayerProperties ctor


@@ -558,6 +580,18 @@ void QgsVectorLayerProperties::apply()
QgsExpressionContextUtils::setLayerVariables( mLayer, mVariableEditor->variablesInActiveScope() );
updateVariableEditor();

// save layer dependencies
QSet<QString> deps;
Q_FOREACH ( const QgsLayerTreeLayer* layer, mLayersDependenciesTreeGroup->findLayers() )
{
if ( layer->isVisible() )
deps << layer->layerId();
}
if ( ! mLayer->setDataDependencies( deps ) )
{
QMessageBox::warning( nullptr, tr( "Dependency cycle" ), tr( "This configuration introduces a cycle in data dependencies and will be ignored" ) );
}

// update symbology
emit refreshLegend( mLayer->id() );

@@ -25,6 +25,8 @@
#include "qgscontexthelp.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsvectorlayer.h"
#include "layertree/qgslayertreemodel.h"
#include "layertree/qgslayertreegroup.h"

class QgsMapLayer;

@@ -193,6 +195,9 @@ class APP_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private

QgsExpressionContext createExpressionContext() const override;

QScopedPointer<QgsLayerTreeGroup> mLayersDependenciesTreeGroup;
QScopedPointer<QgsLayerTreeModel> mLayersDependenciesTreeModel;

private slots:
void openPanel( QgsPanelWidget* panel );
};
@@ -111,6 +111,22 @@ bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsLayerTreeGrou
joinNode.toElement().setAttribute( "joinLayerId", newid );
}
}

// change IDs of dependencies
QDomNodeList dataDeps = doc.elementsByTagName( "dataDependencies" );
for ( int i = 0; i < dataDeps.size(); i++ )
{
QDomNodeList layers = dataDeps.at( i ).childNodes();
for ( int j = 0; j < layers.size(); j++ )
{
QDomElement elt = layers.at( j ).toElement();
if ( elt.attribute( "id" ) == oldid )
{
elt.setAttribute( "id", newid );
}
}
}

}

QDomElement layerTreeElem = doc.documentElement().firstChildElement( "layer-tree-group" );

0 comments on commit bd3cf76

Please sign in to comment.
You can’t perform that action at this time.