Skip to content

Commit

Permalink
Weak relations and auto-restore when loading a style
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Dec 19, 2019
1 parent 76f9d16 commit 90ac90b
Show file tree
Hide file tree
Showing 14 changed files with 453 additions and 18 deletions.
1 change: 1 addition & 0 deletions python/core/auto_generated/qgsmaplayer.sip.in
Expand Up @@ -91,6 +91,7 @@ This is the base class for all map layer types (vector, raster).
Rendering,
CustomProperties,
GeometryOptions,
Relations,
AllStyleCategories
};
typedef QFlags<QgsMapLayer::StyleCategory> StyleCategories;
Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsrelationmanager.sip.in
Expand Up @@ -104,7 +104,7 @@ Gets all relations where the specified layer (and field) is the referencing part
:return: A list of relations matching the given layer and fieldIdx.
%End

QList<QgsRelation> referencedRelations( QgsVectorLayer *layer = 0 ) const;
QList<QgsRelation> referencedRelations( const QgsVectorLayer *layer = 0 ) const;
%Docstring
Gets all relations where this layer is the referenced part (i.e. the parent table with the primary key being referenced from another layer).

Expand Down
2 changes: 2 additions & 0 deletions python/core/auto_generated/qgsvectorlayer.sip.in
Expand Up @@ -2014,6 +2014,8 @@ Returns the layer's relations, where the foreign key is on this layer.
:return: A list of relations
%End



QgsVectorLayerEditBuffer *editBuffer();
%Docstring
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
Expand Down
98 changes: 92 additions & 6 deletions src/app/qgisapp.cpp
Expand Up @@ -75,9 +75,11 @@

#include "qgssettings.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsrelationmanager.h"
#include "qgsapplication.h"
#include "qgslayerstylingwidget.h"
#include "qgstaskmanager.h"
#include "qgsweakrelation.h"
#include "qgsziputils.h"
#include "qgsbrowserguimodel.h"
#include "qgsvectorlayerjoinbuffer.h"
Expand Down Expand Up @@ -667,13 +669,24 @@ void QgisApp::onActiveLayerChanged( QgsMapLayer *layer )

void QgisApp::vectorLayerStyleLoaded( QgsMapLayer::StyleCategories categories )
{
if ( categories.testFlag( QgsMapLayer::StyleCategory::Forms ) )

QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( sender() );

if ( vl && vl->isValid( ) )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( sender() );
if ( vl && vl->isValid( ) )

// Check broken dependencies in forms
if ( categories.testFlag( QgsMapLayer::StyleCategory::Forms ) )
{
checkVectorLayerDependencies( vl );
}

// Check broken relations and try to restore them
if ( categories.testFlag( QgsMapLayer::StyleCategory::Relations ) )
{
checkVectorLayerRelations( vl );
}

}
}

Expand Down Expand Up @@ -1995,17 +2008,18 @@ QgsMessageBar *QgisApp::visibleMessageBar()
}
}

QList<QgsVectorLayerRef> QgisApp::findBrokenWidgetDependencies( QgsVectorLayer *vl )
const QList<QgsVectorLayerRef> QgisApp::findBrokenLayerDependencies( QgsVectorLayer *vl ) const
{
QList<QgsVectorLayerRef> brokenDependencies;

// Check for missing layer widget dependencies
for ( int i = 0; i < vl->fields().count(); i++ )
{
const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( vl, vl->fields().field( i ).name() );
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( fieldFormatter )
{
const auto constDependencies { fieldFormatter->layerDependencies( setup.config() ) };
const QList<QgsVectorLayerRef> constDependencies { fieldFormatter->layerDependencies( setup.config() ) };
for ( const QgsVectorLayerRef &dependency : constDependencies )
{
const QgsVectorLayer *depVl { QgsVectorLayerRef( dependency ).resolveWeakly(
Expand All @@ -2018,14 +2032,61 @@ QList<QgsVectorLayerRef> QgisApp::findBrokenWidgetDependencies( QgsVectorLayer *
}
}
}

// Check for layer weak relations
const QList<QgsWeakRelation> constWeakRelations { vl->weakRelations() };
for ( const QgsWeakRelation &rel : constWeakRelations )
{
QgsRelation relation { rel.resolvedRelation( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ) };
QgsVectorLayerRef dependency;
bool found = false;
if ( ! relation.isValid() )
{
// We need just the other side of the relation
if ( relation.referencedLayer() == vl )
{
dependency = rel.referencingLayer();
found = true;
}
else if ( relation.referencingLayer() == vl )
{
dependency = rel.referencedLayer();
found = true;
}
else
{
// Something wrong is going on here, maybe this relation
// does not really apply to this layer?
QgsMessageLog::logMessage( tr( "None of the layers in the relation stored in the style match the current layer, skipping relation id: %1." ).arg( relation.id() ) );
}

if ( found )
{
// Make sure we don't add it twice
bool refFound = false;
for ( const QgsVectorLayerRef &otherRef : qgis::as_const( brokenDependencies ) )
{
if ( dependency.layerId == otherRef.layerId || ( dependency.source == otherRef.source && dependency.provider == otherRef.provider ) )
{
refFound = true;
break;
}
}
if ( ! refFound )
{
brokenDependencies.append( dependency );
}
}
}
}
return brokenDependencies;
}

void QgisApp::checkVectorLayerDependencies( QgsVectorLayer *vl )
{
if ( vl && vl->isValid() )
{
const auto constDependencies { findBrokenWidgetDependencies( vl ) };
const auto constDependencies { findBrokenLayerDependencies( vl ) };
for ( const QgsVectorLayerRef &dependency : constDependencies )
{
// try to aggressively resolve the broken dependencies
Expand Down Expand Up @@ -2115,6 +2176,31 @@ void QgisApp::checkVectorLayerDependencies( QgsVectorLayer *vl )
}
}

void QgisApp::checkVectorLayerRelations( QgsVectorLayer *vectorLayer )
{
if ( vectorLayer && vectorLayer->isValid() )
{
const QList<QgsWeakRelation> constWeakRelations { vectorLayer->weakRelations( ) };
for ( const QgsWeakRelation &rel : constWeakRelations )
{
QgsRelation relation { rel.resolvedRelation( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ) };
if ( relation.isValid() )
{
// Avoid duplicates
const QList<QgsRelation> constRelations { QgsProject::instance()->relationManager()->relations().values() };
for ( const QgsRelation &other : constRelations )
{
if ( relation.hasEqualDefinition( other ) )
{
continue;
}
}
QgsProject::instance()->relationManager()->addRelation( relation );
}
}
}
}

void QgisApp::dataSourceManager( const QString &pageName )
{
if ( ! mDataSourceManagerDialog )
Expand Down
18 changes: 14 additions & 4 deletions src/app/qgisapp.h
Expand Up @@ -2025,16 +2025,26 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
QgsMessageBar *visibleMessageBar();

/**
* Searches for layer widget dependencies
* \return a list of weak references to broken widget layer dependencies
* Searches for layer dependencies by querying widgets and the layer itself for broken relations
* \return a list of weak references to broken layer dependencies
*/
QList< QgsVectorLayerRef > findBrokenWidgetDependencies( QgsVectorLayer *vectorLayer );
const QList< QgsVectorLayerRef > findBrokenLayerDependencies( QgsVectorLayer *vectorLayer ) const;

/**
* Scans the \a vectorLayer for broken dependencies and warns the user
* Scans the \a vectorLayer for broken dependencies and automatically
* try to load the missing layer and warns the user about the operation
* result.
*/
void checkVectorLayerDependencies( QgsVectorLayer *vectorLayer );

/**
* Scans the \a vectorLayer for broken relations and automatically
* try to create the missing relation and warns the user about the operation
* result.
*/
void checkVectorLayerRelations( QgsVectorLayer *vectorLayer );


QgisAppStyleSheet *mStyleSheetBuilder = nullptr;


Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -339,6 +339,7 @@ SET(QGIS_CORE_SRCS
qgsreadwritecontext.cpp
qgsreadwritelocker.cpp
qgsrelation.cpp
qgsweakrelation.cpp
qgsrelationmanager.cpp
qgsrenderchecker.cpp
qgsrendercontext.cpp
Expand Down Expand Up @@ -840,6 +841,7 @@ SET(QGIS_CORE_HDRS
qgsreadwritecontext.h
qgsreadwritelocker.h
qgsrelation.h
qgsweakrelation.h
qgsrelationmanager.h
qgsrenderchecker.h
qgsrendercontext.h
Expand Down
7 changes: 3 additions & 4 deletions src/core/qgsmaplayer.cpp
Expand Up @@ -267,9 +267,6 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteCon

QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Layer" ), mne.text() );

// now let the children grab what they need from the Dom node.
layerError = !readXml( layerElement, context );

// overwrite CRS with what we read from project file before the raster/vector
// file reading functions changed it. They will if projections is specified in the file.
// FIXME: is this necessary?
Expand All @@ -296,14 +293,16 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteCon
setRefreshOnNofifyMessage( layerElement.attribute( QStringLiteral( "refreshOnNotifyMessage" ), QString() ) );
setRefreshOnNotifyEnabled( layerElement.attribute( QStringLiteral( "refreshOnNotifyEnabled" ), QStringLiteral( "0" ) ).toInt() );


// set name
mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
mne = mnl.toElement();

//name can be translated
setName( context.projectTranslator()->translate( QStringLiteral( "project:layers:%1" ).arg( layerElement.namedItem( QStringLiteral( "id" ) ).toElement().text() ), mne.text() ) );

// now let the children grab what they need from the Dom node.
layerError = !readXml( layerElement, context );

//short name
QDomElement shortNameElem = layerElement.firstChildElement( QStringLiteral( "shortname" ) );
if ( !shortNameElem.isNull() )
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsmaplayer.h
Expand Up @@ -161,8 +161,9 @@ class CORE_EXPORT QgsMapLayer : public QObject
Rendering = 1 << 10, //!< Rendering: scale visibility, simplify method, opacity
CustomProperties = 1 << 11, //!< Custom properties (by plugins for instance)
GeometryOptions = 1 << 12, //!< Geometry validation configuration
Relations = 1 << 13, //!< Relations
AllStyleCategories = LayerConfiguration | Symbology | Symbology3D | Labeling | Fields | Forms | Actions |
MapTips | Diagrams | AttributeTable | Rendering | CustomProperties | GeometryOptions,
MapTips | Diagrams | AttributeTable | Rendering | CustomProperties | GeometryOptions | Relations,
};
Q_ENUM( StyleCategory )
Q_DECLARE_FLAGS( StyleCategories, StyleCategory )
Expand Down
5 changes: 4 additions & 1 deletion src/core/qgsrelationmanager.cpp
Expand Up @@ -27,8 +27,11 @@ QgsRelationManager::QgsRelationManager( QgsProject *project )
{
if ( mProject )
{
// TODO: QGIS 4 remove: relations are now stored with the layer style
connect( project, &QgsProject::readProjectWithContext, this, &QgsRelationManager::readProject );
// TODO: QGIS 4 remove: relations are now stored with the layer style
connect( project, &QgsProject::writeProject, this, &QgsRelationManager::writeProject );

connect( project, &QgsProject::layersRemoved, this, &QgsRelationManager::layersRemoved );
}
}
Expand Down Expand Up @@ -148,7 +151,7 @@ QList<QgsRelation> QgsRelationManager::referencingRelations( const QgsVectorLaye
return relations;
}

QList<QgsRelation> QgsRelationManager::referencedRelations( QgsVectorLayer *layer ) const
QList<QgsRelation> QgsRelationManager::referencedRelations( const QgsVectorLayer *layer ) const
{
if ( !layer )
{
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsrelationmanager.h
Expand Up @@ -121,7 +121,7 @@ class CORE_EXPORT QgsRelationManager : public QObject
*
* \returns A list of relations where the specified layer is the referenced part.
*/
QList<QgsRelation> referencedRelations( QgsVectorLayer *layer = nullptr ) const;
QList<QgsRelation> referencedRelations( const QgsVectorLayer *layer = nullptr ) const;

/**
* Discover all the relations available from the current layers.
Expand Down

0 comments on commit 90ac90b

Please sign in to comment.