Skip to content

Commit

Permalink
Merge pull request #33283 from elpaso/relation-style-restore
Browse files Browse the repository at this point in the history
Relation style restore
  • Loading branch information
elpaso committed Dec 19, 2019
2 parents 76f9d16 + a4859a8 commit 6654870
Show file tree
Hide file tree
Showing 19 changed files with 661 additions and 53 deletions.
1 change: 1 addition & 0 deletions images/images.qrc
Expand Up @@ -591,6 +591,7 @@
<file>themes/default/propertyicons/system.svg</file>
<file>themes/default/propertyicons/transparency.svg</file>
<file>themes/default/propertyicons/spacer.svg</file>
<file>themes/default/propertyicons/relations.svg</file>
<file>themes/default/rendererCategorizedSymbol.svg</file>
<file>themes/default/rendererGraduatedSymbol.svg</file>
<file>themes/default/rendererNullSymbol.svg</file>
Expand Down
31 changes: 31 additions & 0 deletions images/themes/default/propertyicons/relations.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
138 changes: 118 additions & 20 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 ) )
{
resolveVectorLayerDependencies( vl );
}

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

}
}

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

QList<QgsVectorLayerRef> QgisApp::findBrokenWidgetDependencies( QgsVectorLayer *vl )
const QList<QgsVectorLayerRef> QgisApp::findBrokenLayerDependencies( QgsVectorLayer *vl, QgsMapLayer::StyleCategories categories ) const
{
QList<QgsVectorLayerRef> brokenDependencies;
// Check for missing layer widget dependencies
for ( int i = 0; i < vl->fields().count(); i++ )

if ( categories.testFlag( QgsMapLayer::StyleCategory::Forms ) )
{
const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( vl, vl->fields().field( i ).name() );
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( fieldFormatter )
for ( int i = 0; i < vl->fields().count(); i++ )
{
const auto constDependencies { fieldFormatter->layerDependencies( setup.config() ) };
for ( const QgsVectorLayerRef &dependency : constDependencies )
const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( vl, vl->fields().field( i ).name() );
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
if ( fieldFormatter )
{
const QgsVectorLayer *depVl { QgsVectorLayerRef( dependency ).resolveWeakly(
QgsProject::instance(),
QgsVectorLayerRef::MatchType::Name ) };
if ( ! depVl || ! depVl->isValid() )
const QList<QgsVectorLayerRef> constDependencies { fieldFormatter->layerDependencies( setup.config() ) };
for ( const QgsVectorLayerRef &dependency : constDependencies )
{
const QgsVectorLayer *depVl { QgsVectorLayerRef( dependency ).resolveWeakly(
QgsProject::instance(),
QgsVectorLayerRef::MatchType::Name ) };
if ( ! depVl || ! depVl->isValid() )
{
brokenDependencies.append( dependency );
}
}
}
}
}

if ( categories.testFlag( QgsMapLayer::StyleCategory::Relations ) )
{
// 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() )
{
// This is the big question: do we really
// want to automatically load the referencing layer(s) too?
// This could potentially lead to a cascaded load of a
// long list of layers.
// The code is in place but let's leave it disabled for now.
if ( relation.referencedLayer() == vl )
{
// Do nothing because vl is the referenced layer
#if 0
dependency = rel.referencingLayer();
found = true;
#endif
}
else if ( relation.referencingLayer() == vl )
{
brokenDependencies.append( dependency );
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 )
void QgisApp::resolveVectorLayerDependencies( QgsVectorLayer *vl, QgsMapLayer::StyleCategories categories )
{
if ( vl && vl->isValid() )
{
const auto constDependencies { findBrokenWidgetDependencies( vl ) };
const auto constDependencies { findBrokenLayerDependencies( vl, categories ) };
for ( const QgsVectorLayerRef &dependency : constDependencies )
{
// try to aggressively resolve the broken dependencies
Expand Down Expand Up @@ -2115,6 +2188,31 @@ void QgisApp::checkVectorLayerDependencies( QgsVectorLayer *vl )
}
}

void QgisApp::resolveVectorLayerWeakRelations( 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 Expand Up @@ -6506,7 +6604,7 @@ bool QgisApp::addProject( const QString &projectFile )
{
if ( vl->isValid() )
{
checkVectorLayerDependencies( vl );
resolveVectorLayerDependencies( vl );
}
}

Expand Down
26 changes: 21 additions & 5 deletions src/app/qgisapp.h
Expand Up @@ -2025,15 +2025,31 @@ 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 the form widgets and the
* \a vectorLayer itself for broken relations. Style \a categories can be
* used to limit the search to one or more of the currently implemented search
* categories ("Forms" for the form widgets and "Relations" for layer weak relations).
* \return a list of weak references to broken layer dependencies
*/
QList< QgsVectorLayerRef > findBrokenWidgetDependencies( QgsVectorLayer *vectorLayer );
const QList< QgsVectorLayerRef > findBrokenLayerDependencies( QgsVectorLayer *vectorLayer,
QgsMapLayer::StyleCategories categories = QgsMapLayer::StyleCategory::AllStyleCategories ) 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 layers, users are notified about the operation
* result. Style \a categories can be
* used to exclude one of the currently implemented search categories
* ("Forms" for the form widgets and "Relations" for layer weak relations).
*/
void checkVectorLayerDependencies( QgsVectorLayer *vectorLayer );
void resolveVectorLayerDependencies( QgsVectorLayer *vectorLayer,
QgsMapLayer::StyleCategories categories = QgsMapLayer::AllStyleCategories );

/**
* Scans the \a vectorLayer for weak relations and automatically
* try to resolve and create the broken relations.
*/
void resolveVectorLayerWeakRelations( QgsVectorLayer *vectorLayer );


QgisAppStyleSheet *mStyleSheetBuilder = nullptr;

Expand Down

0 comments on commit 6654870

Please sign in to comment.