mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-28 00:17:30 -05:00
Merge pull request #33283 from elpaso/relation-style-restore
Relation style restore
This commit is contained in:
commit
6654870c0b
@ -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>
|
||||
|
31
images/themes/default/propertyicons/relations.svg
Normal file
31
images/themes/default/propertyicons/relations.svg
Normal file
@ -0,0 +1,31 @@
|
||||
<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="5.6324644" x2="10.6324644" y1="3.7047943" y2="15.7047943">
|
||||
<stop offset="0" stop-color="#f1f1f1"/>
|
||||
<stop offset="1" stop-color="#d6d6d6"/>
|
||||
</linearGradient>
|
||||
<path d="m10.283011 8.3578359l10.139192.00203v7.8920306" fill="none" stroke="#2b3b4d"/>
|
||||
<g fill-rule="evenodd" stroke="#888a85" stroke-linecap="round" stroke-linejoin="round" transform="translate(-.431455 -.14381833)">
|
||||
<rect fill="url(#a)" height="15" overflow="visible" rx="1" width="13" x="2.6324642" y="1.7047944"/>
|
||||
<path d="m2.6324644 5.7047943l12.9999996 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.6324644 8.7047943l2 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.6324644 11.704794l2 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.6324644 14.704795l2 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.6324641 8.7047943l3.9999999 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.5703417 11.663069l4.0621223.04172" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.6324641 14.704795l3.9999999 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.7035514 3.7105107l1.928913-.00572" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.6324641 3.7047943l3.9999999 0" fill="#eeeeec" overflow="visible"/>
|
||||
</g>
|
||||
<g fill-rule="evenodd" stroke="#888a85" stroke-linecap="round" stroke-linejoin="round" transform="translate(14.885197 13.80656)">
|
||||
<rect fill="url(#a)" height="15" overflow="visible" rx="1" width="13" x="2.6324642" y="1.7047944"/>
|
||||
<path d="m2.6324644 5.7047943l12.9999996 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.6324644 8.7047943l2 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.6324644 11.704794l2 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.6324644 14.704795l2 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.6324641 8.7047943l3.9999999 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.5703417 11.663069l4.0621223.04172" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.6324641 14.704795l3.9999999 0" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m4.7035514 3.7105107l1.928913-.00572" fill="#eeeeec" overflow="visible"/>
|
||||
<path d="m9.6324641 3.7047943l3.9999999 0" fill="#eeeeec" overflow="visible"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
@ -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;
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
@ -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 );
|
||||
resolveVectorLayerDependencies( vl );
|
||||
}
|
||||
|
||||
// Check broken relations and try to restore them
|
||||
if ( categories.testFlag( QgsMapLayer::StyleCategory::Relations ) )
|
||||
{
|
||||
resolveVectorLayerWeakRelations( vl );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1995,25 +2008,85 @@ 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 )
|
||||
{
|
||||
brokenDependencies.append( dependency );
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2021,11 +2094,11 @@ QList<QgsVectorLayerRef> QgisApp::findBrokenWidgetDependencies( QgsVectorLayer *
|
||||
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
|
||||
@ -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 )
|
||||
@ -6506,7 +6604,7 @@ bool QgisApp::addProject( const QString &projectFile )
|
||||
{
|
||||
if ( vl->isValid() )
|
||||
{
|
||||
checkVectorLayerDependencies( vl );
|
||||
resolveVectorLayerDependencies( vl );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -77,7 +77,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
|
||||
switch ( category )
|
||||
{
|
||||
case QgsMapLayer::LayerConfiguration:
|
||||
case QgsMapLayer::StyleCategory::LayerConfiguration:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -88,7 +88,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/system.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Symbology:
|
||||
case QgsMapLayer::StyleCategory::Symbology:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -99,7 +99,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/symbology.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Symbology3D:
|
||||
case QgsMapLayer::StyleCategory::Symbology3D:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -110,7 +110,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/3d.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Labeling:
|
||||
case QgsMapLayer::StyleCategory::Labeling:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -121,7 +121,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/labels.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Fields:
|
||||
case QgsMapLayer::StyleCategory::Fields:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -132,7 +132,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/mSourceFields.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Forms:
|
||||
case QgsMapLayer::StyleCategory::Forms:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -143,7 +143,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFormView.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Actions:
|
||||
case QgsMapLayer::StyleCategory::Actions:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -154,7 +154,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/action.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::MapTips:
|
||||
case QgsMapLayer::StyleCategory::MapTips:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -165,7 +165,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/display.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Diagrams:
|
||||
case QgsMapLayer::StyleCategory::Diagrams:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -176,7 +176,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/diagram.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::AttributeTable:
|
||||
case QgsMapLayer::StyleCategory::AttributeTable:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -187,7 +187,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::Rendering:
|
||||
case QgsMapLayer::StyleCategory::Rendering:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -198,7 +198,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/rendering.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::CustomProperties:
|
||||
case QgsMapLayer::StyleCategory::CustomProperties:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -209,7 +209,7 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/mActionOptions.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::GeometryOptions:
|
||||
case QgsMapLayer::StyleCategory::GeometryOptions:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
@ -220,7 +220,18 @@ QVariant QgsMapLayerStyleCategoriesModel::data( const QModelIndex &index, int ro
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/digitizing.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::AllStyleCategories:
|
||||
case QgsMapLayer::StyleCategory::Relations:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
return tr( "Relations" );
|
||||
case Qt::ToolTipRole:
|
||||
return tr( "Relations with other layers" );
|
||||
case Qt::DecorationRole:
|
||||
return QgsApplication::getThemeIcon( QStringLiteral( "/propertyicons/relations.svg" ) );
|
||||
}
|
||||
break;
|
||||
case QgsMapLayer::StyleCategory::AllStyleCategories:
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
|
@ -339,6 +339,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgsreadwritecontext.cpp
|
||||
qgsreadwritelocker.cpp
|
||||
qgsrelation.cpp
|
||||
qgsweakrelation.cpp
|
||||
qgsrelationmanager.cpp
|
||||
qgsrenderchecker.cpp
|
||||
qgsrendercontext.cpp
|
||||
@ -840,6 +841,7 @@ SET(QGIS_CORE_HDRS
|
||||
qgsreadwritecontext.h
|
||||
qgsreadwritelocker.h
|
||||
qgsrelation.h
|
||||
qgsweakrelation.h
|
||||
qgsrelationmanager.h
|
||||
qgsrenderchecker.h
|
||||
qgsrendercontext.h
|
||||
|
@ -267,15 +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?
|
||||
QgsCoordinateReferenceSystem::setCustomCrsValidation( savedValidation );
|
||||
mCRS = savedCRS;
|
||||
|
||||
// the internal name is just the data source basename
|
||||
//QFileInfo dataSourceFileInfo( mDataSource );
|
||||
//internalName = dataSourceFileInfo.baseName();
|
||||
@ -296,7 +287,6 @@ 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();
|
||||
@ -304,6 +294,15 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteCon
|
||||
//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 );
|
||||
|
||||
// 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? Yes, it is (autumn 2019)
|
||||
QgsCoordinateReferenceSystem::setCustomCrsValidation( savedValidation );
|
||||
mCRS = savedCRS;
|
||||
|
||||
//short name
|
||||
QDomElement shortNameElem = layerElement.firstChildElement( QStringLiteral( "shortname" ) );
|
||||
if ( !shortNameElem.isNull() )
|
||||
|
@ -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 )
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
@ -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 )
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include "qgsproviderregistry.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgsrelationmanager.h"
|
||||
#include "qgsweakrelation.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsvectorlayereditbuffer.h"
|
||||
@ -2026,6 +2027,80 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes
|
||||
updateFields();
|
||||
}
|
||||
|
||||
if ( categories.testFlag( Relations ) )
|
||||
{
|
||||
|
||||
const QgsPathResolver resolver { QgsProject::instance()->pathResolver() };
|
||||
|
||||
// Restore referenced layers: relations where "this" is the child layer (the referencing part, that holds the FK)
|
||||
QDomNodeList referencedLayersNodeList = layerNode.toElement().elementsByTagName( QStringLiteral( "referencedLayers" ) );
|
||||
if ( referencedLayersNodeList.size() > 0 )
|
||||
{
|
||||
const QDomNodeList relationNodes { referencedLayersNodeList.at( 0 ).childNodes() };
|
||||
for ( int i = 0; i < relationNodes.length(); ++i )
|
||||
{
|
||||
const QDomElement relationElement = relationNodes.at( i ).toElement();
|
||||
QList<QgsRelation::FieldPair> fieldPairs;
|
||||
const QDomNodeList fieldPairNodes { relationElement.elementsByTagName( QStringLiteral( "fieldPair" ) ) };
|
||||
for ( int j = 0; j < fieldPairNodes.length(); ++j )
|
||||
{
|
||||
const QDomElement fieldPairElement = fieldPairNodes.at( i ).toElement();
|
||||
fieldPairs.push_back( { fieldPairElement.attribute( QStringLiteral( "referencing" ) ),
|
||||
fieldPairElement.attribute( QStringLiteral( "referenced" ) ) } );
|
||||
}
|
||||
mWeakRelations.push_back( QgsWeakRelation { relationElement.attribute( QStringLiteral( "id" ) ),
|
||||
relationElement.attribute( QStringLiteral( "name" ) ),
|
||||
static_cast<QgsRelation::RelationStrength>( relationElement.attribute( QStringLiteral( "strength" ) ).toInt() ),
|
||||
// Referencing
|
||||
id(),
|
||||
name(),
|
||||
resolver.writePath( publicSource() ),
|
||||
providerType(),
|
||||
// Referenced
|
||||
relationElement.attribute( QStringLiteral( "layerId" ) ),
|
||||
relationElement.attribute( QStringLiteral( "layerName" ) ),
|
||||
relationElement.attribute( QStringLiteral( "dataSource" ) ),
|
||||
relationElement.attribute( QStringLiteral( "providerKey" ) ),
|
||||
fieldPairs
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
// Restore referencing layers: relations where "this" is the parent layer (the referenced part where the FK points to)
|
||||
QDomNodeList referencingLayersNodeList = layerNode.toElement().elementsByTagName( QStringLiteral( "referencingLayers" ) );
|
||||
if ( referencingLayersNodeList.size() > 0 )
|
||||
{
|
||||
const QDomNodeList relationNodes { referencingLayersNodeList.at( 0 ).childNodes() };
|
||||
for ( int i = 0; i < relationNodes.length(); ++i )
|
||||
{
|
||||
const QDomElement relationElement = relationNodes.at( i ).toElement();
|
||||
QList<QgsRelation::FieldPair> fieldPairs;
|
||||
const QDomNodeList fieldPairNodes { relationElement.elementsByTagName( QStringLiteral( "fieldPair" ) ) };
|
||||
for ( int j = 0; j < fieldPairNodes.length(); ++j )
|
||||
{
|
||||
const QDomElement fieldPairElement = fieldPairNodes.at( i ).toElement();
|
||||
fieldPairs.push_back( { fieldPairElement.attribute( QStringLiteral( "referencing" ) ),
|
||||
fieldPairElement.attribute( QStringLiteral( "referenced" ) ) } );
|
||||
}
|
||||
mWeakRelations.push_back( QgsWeakRelation { relationElement.attribute( QStringLiteral( "id" ) ),
|
||||
relationElement.attribute( QStringLiteral( "name" ) ),
|
||||
static_cast<QgsRelation::RelationStrength>( relationElement.attribute( QStringLiteral( "strength" ) ).toInt() ),
|
||||
// Referencing
|
||||
relationElement.attribute( QStringLiteral( "layerId" ) ),
|
||||
relationElement.attribute( QStringLiteral( "layerName" ) ),
|
||||
relationElement.attribute( QStringLiteral( "dataSource" ) ),
|
||||
relationElement.attribute( QStringLiteral( "providerKey" ) ),
|
||||
// Referenced
|
||||
id(),
|
||||
name(),
|
||||
resolver.writePath( publicSource() ),
|
||||
providerType(),
|
||||
fieldPairs
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDomElement layerElement = layerNode.toElement();
|
||||
|
||||
readCommonStyle( layerElement, context, categories );
|
||||
@ -2445,6 +2520,74 @@ bool QgsVectorLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString
|
||||
if ( categories.testFlag( GeometryOptions ) )
|
||||
mGeometryOptions->writeXml( node );
|
||||
|
||||
|
||||
// Relation information for both referenced and referencing sides
|
||||
if ( categories.testFlag( Relations ) )
|
||||
{
|
||||
|
||||
const QgsPathResolver resolver { QgsProject::instance()->pathResolver() };
|
||||
|
||||
// Store referenced layers: relations where "this" is the child layer (the referencing part, that holds the FK)
|
||||
QDomElement referencedLayersElement = doc.createElement( QStringLiteral( "referencedLayers" ) );
|
||||
node.appendChild( referencedLayersElement );
|
||||
|
||||
const auto constReferencingRelations { QgsProject::instance()->relationManager()->referencingRelations( this ) };
|
||||
for ( const auto &rel : constReferencingRelations )
|
||||
{
|
||||
|
||||
QDomElement relationElement = doc.createElement( QStringLiteral( "relation" ) );
|
||||
referencedLayersElement.appendChild( relationElement );
|
||||
|
||||
relationElement.setAttribute( QStringLiteral( "id" ), rel.id() );
|
||||
relationElement.setAttribute( QStringLiteral( "name" ), rel.name() );
|
||||
relationElement.setAttribute( QStringLiteral( "strength" ), rel.strength() );
|
||||
relationElement.setAttribute( QStringLiteral( "layerId" ), rel.referencedLayer()->id() );
|
||||
relationElement.setAttribute( QStringLiteral( "layerName" ), rel.referencedLayer()->name() );
|
||||
relationElement.setAttribute( QStringLiteral( "dataSource" ), resolver.writePath( rel.referencedLayer()->publicSource() ) );
|
||||
relationElement.setAttribute( QStringLiteral( "providerKey" ), rel.referencedLayer()->providerType() );
|
||||
|
||||
const QList<QgsRelation::FieldPair> constFieldPairs { rel.fieldPairs() };
|
||||
for ( const QgsRelation::FieldPair &fp : constFieldPairs )
|
||||
{
|
||||
QDomElement fieldPair = doc.createElement( QStringLiteral( "fieldPair" ) );
|
||||
relationElement.appendChild( fieldPair );
|
||||
fieldPair.setAttribute( QStringLiteral( "referenced" ), fp.referencedField() );
|
||||
fieldPair.setAttribute( QStringLiteral( "referencing" ), fp.referencingField() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Store referencing layers: relations where "this" is the parent layer (the referenced part where the FK points to)
|
||||
QDomElement referencingLayersElement = doc.createElement( QStringLiteral( "referencingLayers" ) );
|
||||
node.appendChild( referencingLayersElement );
|
||||
|
||||
const auto constReferencedRelations { QgsProject::instance()->relationManager()->referencedRelations( this ) };
|
||||
for ( const auto &rel : constReferencedRelations )
|
||||
{
|
||||
|
||||
QDomElement relationElement = doc.createElement( QStringLiteral( "relation" ) );
|
||||
referencingLayersElement.appendChild( relationElement );
|
||||
|
||||
relationElement.setAttribute( QStringLiteral( "id" ), rel.id() );
|
||||
relationElement.setAttribute( QStringLiteral( "name" ), rel.name() );
|
||||
relationElement.setAttribute( QStringLiteral( "strength" ), rel.strength() );
|
||||
relationElement.setAttribute( QStringLiteral( "layerId" ), rel.referencingLayer()->id() );
|
||||
relationElement.setAttribute( QStringLiteral( "layerName" ), rel.referencingLayer()->name() );
|
||||
relationElement.setAttribute( QStringLiteral( "dataSource" ), resolver.writePath( rel.referencingLayer()->publicSource() ) );
|
||||
relationElement.setAttribute( QStringLiteral( "providerKey" ), rel.referencingLayer()->providerType() );
|
||||
|
||||
const QList<QgsRelation::FieldPair> constFieldPairs { rel.fieldPairs() };
|
||||
for ( const QgsRelation::FieldPair &fp : constFieldPairs )
|
||||
{
|
||||
QDomElement fieldPair = doc.createElement( QStringLiteral( "fieldPair" ) );
|
||||
relationElement.appendChild( fieldPair );
|
||||
fieldPair.setAttribute( QStringLiteral( "referenced" ), fp.referencedField() );
|
||||
fieldPair.setAttribute( QStringLiteral( "referencing" ), fp.referencingField() );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( categories.testFlag( Fields ) )
|
||||
{
|
||||
QDomElement fieldConfigurationElement = doc.createElement( QStringLiteral( "fieldConfiguration" ) );
|
||||
@ -4766,6 +4909,11 @@ QList<QgsRelation> QgsVectorLayer::referencingRelations( int idx ) const
|
||||
return QgsProject::instance()->relationManager()->referencingRelations( this, idx );
|
||||
}
|
||||
|
||||
QList<QgsWeakRelation> QgsVectorLayer::weakRelations() const
|
||||
{
|
||||
return mWeakRelations;
|
||||
}
|
||||
|
||||
int QgsVectorLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
|
||||
{
|
||||
return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
|
||||
|
@ -62,6 +62,7 @@ class QgsMapToPixel;
|
||||
class QgsRectangle;
|
||||
class QgsRectangle;
|
||||
class QgsRelation;
|
||||
class QgsWeakRelation;
|
||||
class QgsRelationManager;
|
||||
class QgsSingleSymbolRenderer;
|
||||
class QgsStoredExpressionManager;
|
||||
@ -1872,6 +1873,15 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
*/
|
||||
QList<QgsRelation> referencingRelations( int idx ) const;
|
||||
|
||||
/**
|
||||
* Returns the layer's weak relations as specified in the layer's style.
|
||||
* \returns A list of weak relations
|
||||
* \note not available in Python bindings
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
QList<QgsWeakRelation> weakRelations( ) const SIP_SKIP;
|
||||
|
||||
|
||||
//! Buffer with uncommitted editing operations. Only valid after editing has been turned on.
|
||||
Q_INVOKABLE QgsVectorLayerEditBuffer *editBuffer() { return mEditBuffer; }
|
||||
|
||||
@ -2827,6 +2837,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
|
||||
//! To avoid firing multiple time dataChanged signal on circular layer circular dependencies
|
||||
bool mDataChangedFired = false;
|
||||
|
||||
QList<QgsWeakRelation> mWeakRelations;
|
||||
};
|
||||
|
||||
|
||||
|
75
src/core/qgsweakrelation.cpp
Normal file
75
src/core/qgsweakrelation.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/***************************************************************************
|
||||
qgsweakrelation.cpp - QgsWeakRelation
|
||||
|
||||
---------------------
|
||||
begin : 5.12.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgsweakrelation.h"
|
||||
|
||||
|
||||
QgsWeakRelation::QgsWeakRelation( const QString &relationId, const QString &relationName, const QgsRelation::RelationStrength strength,
|
||||
const QString &referencingLayerId, const QString &referencingLayerName, const QString &referencingLayerSource, const QString &referencingLayerProviderKey,
|
||||
const QString &referencedLayerId, const QString &referencedLayerName, const QString &referencedLayerSource, const QString &referencedLayerProviderKey,
|
||||
const QList<QgsRelation::FieldPair> &fieldPairs )
|
||||
: mReferencingLayer( referencingLayerId, referencingLayerName, referencingLayerSource, referencingLayerProviderKey )
|
||||
, mReferencedLayer( referencedLayerId, referencedLayerName, referencedLayerSource, referencedLayerProviderKey )
|
||||
, mRelationId( relationId )
|
||||
, mRelationName( relationName )
|
||||
, mStrength( strength )
|
||||
, mFieldPairs( fieldPairs )
|
||||
{
|
||||
}
|
||||
|
||||
QgsRelation QgsWeakRelation::resolvedRelation( const QgsProject *project, QgsVectorLayerRef::MatchType matchType ) const
|
||||
{
|
||||
QgsRelation relation;
|
||||
relation.setId( mRelationId );
|
||||
relation.setName( mRelationName );
|
||||
relation.setStrength( mStrength );
|
||||
QgsVectorLayerRef referencedLayerRef { mReferencedLayer };
|
||||
QgsMapLayer *referencedLayer { referencedLayerRef.resolveWeakly( project, matchType ) };
|
||||
if ( referencedLayer )
|
||||
{
|
||||
relation.setReferencedLayer( referencedLayer->id() );
|
||||
}
|
||||
QgsVectorLayerRef referencingLayerRef { mReferencingLayer };
|
||||
QgsMapLayer *referencingLayer { referencingLayerRef.resolveWeakly( project, matchType ) };
|
||||
if ( referencingLayer )
|
||||
{
|
||||
relation.setReferencingLayer( referencingLayer->id() );
|
||||
}
|
||||
for ( const auto &fp : qgis::as_const( mFieldPairs ) )
|
||||
{
|
||||
relation.addFieldPair( fp );
|
||||
}
|
||||
return relation;
|
||||
}
|
||||
|
||||
QgsVectorLayerRef QgsWeakRelation::referencingLayer() const
|
||||
{
|
||||
return mReferencingLayer;
|
||||
}
|
||||
|
||||
QgsVectorLayerRef QgsWeakRelation::referencedLayer() const
|
||||
{
|
||||
return mReferencedLayer;
|
||||
}
|
||||
|
||||
QgsRelation::RelationStrength QgsWeakRelation::strength() const
|
||||
{
|
||||
return mStrength;
|
||||
}
|
||||
|
||||
QList<QgsRelation::FieldPair> QgsWeakRelation::fieldPairs() const
|
||||
{
|
||||
return mFieldPairs;
|
||||
}
|
101
src/core/qgsweakrelation.h
Normal file
101
src/core/qgsweakrelation.h
Normal file
@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
qgsweakrelation.h - QgsWeakRelation
|
||||
|
||||
---------------------
|
||||
begin : 5.12.2019
|
||||
copyright : (C) 2019 by Alessandro Pasotti
|
||||
email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSWEAKRELATION_H
|
||||
#define QGSWEAKRELATION_H
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgsrelation.h"
|
||||
#include "qgsvectorlayerref.h"
|
||||
|
||||
/**
|
||||
* The QgsWeakRelation class represent a QgsRelation with possibly
|
||||
* unresolved layers or unmatched fields.
|
||||
*
|
||||
* This class is used to store relation information attached to a
|
||||
* layer style, a method to attempt relation resolution is also
|
||||
* implemented and can be used to create a QgsRelation after the
|
||||
* dependent layers are loaded and available.
|
||||
*
|
||||
* \note not available in Python bindings
|
||||
* \ingroup core
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
class CORE_EXPORT QgsWeakRelation
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates a QgsWeakRelation
|
||||
*/
|
||||
QgsWeakRelation( const QString &relationId,
|
||||
const QString &relationName,
|
||||
const QgsRelation::RelationStrength strength,
|
||||
const QString &referencingLayerId,
|
||||
const QString &referencingLayerName,
|
||||
const QString &referencingLayerSource,
|
||||
const QString &referencingLayerProviderKey,
|
||||
const QString &referencedLayerId,
|
||||
const QString &referencedLayerName,
|
||||
const QString &referencedLayerSource,
|
||||
const QString &referencedLayerProviderKey,
|
||||
const QList<QgsRelation::FieldPair> &fieldPairs
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolves a weak relation in the given \a project returning a possibly invalid QgsRelation
|
||||
* and without performing any kind of validity check.
|
||||
*
|
||||
* \note Client code should never assume that the returned relation is valid and the
|
||||
* layer components are not NULL.
|
||||
*/
|
||||
QgsRelation resolvedRelation( const QgsProject *project, QgsVectorLayerRef::MatchType matchType = QgsVectorLayerRef::MatchType::All ) const;
|
||||
|
||||
/**
|
||||
* Returns a weak reference to the referencing layer
|
||||
*/
|
||||
QgsVectorLayerRef referencingLayer() const;
|
||||
|
||||
/**
|
||||
* Returns a weak reference to the referenced layer
|
||||
*/
|
||||
QgsVectorLayerRef referencedLayer() const;
|
||||
|
||||
/**
|
||||
* Returns the strength of the relation
|
||||
*/
|
||||
QgsRelation::RelationStrength strength() const;
|
||||
|
||||
/**
|
||||
* Returns the list of field pairs
|
||||
*/
|
||||
QList<QgsRelation::FieldPair> fieldPairs() const;
|
||||
|
||||
private:
|
||||
|
||||
QgsVectorLayerRef mReferencingLayer;
|
||||
QgsVectorLayerRef mReferencedLayer;
|
||||
QString mRelationId;
|
||||
QString mRelationName;
|
||||
QgsRelation::RelationStrength mStrength = QgsRelation::RelationStrength::Association;
|
||||
QList<QgsRelation::FieldPair> mFieldPairs;
|
||||
|
||||
friend class TestQgsWeakRelation;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSWEAKRELATION_H
|
@ -241,6 +241,7 @@ SET(TESTS
|
||||
testqobjectuniqueptr.cpp
|
||||
testqgspostgresstringutils.cpp
|
||||
testqgsstoredexpressionmanager.cpp
|
||||
testqgsweakrelation.cpp
|
||||
)
|
||||
|
||||
IF(WITH_QTWEBKIT)
|
||||
|
106
tests/src/core/testqgsweakrelation.cpp
Normal file
106
tests/src/core/testqgsweakrelation.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
/***************************************************************************
|
||||
testqgsweakrelation.cpp
|
||||
----------------
|
||||
Date : December 2019
|
||||
Copyright : (C) 2019 Alessandro Pasotti
|
||||
Email : elpaso at itopen dot it
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgstest.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgsweakrelation.h"
|
||||
#include "qgsrelation.h"
|
||||
|
||||
class TestQgsWeakRelation: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init();// will be called before each testfunction is executed.
|
||||
void cleanup();// will be called after every testfunction.
|
||||
|
||||
void testResolved(); // Test if relation can be resolved
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
void TestQgsWeakRelation::initTestCase()
|
||||
{
|
||||
// Set up the QgsSettings environment
|
||||
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
|
||||
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
|
||||
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
}
|
||||
|
||||
void TestQgsWeakRelation::cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void TestQgsWeakRelation::init()
|
||||
{
|
||||
QLocale::setDefault( QLocale::English );
|
||||
}
|
||||
|
||||
void TestQgsWeakRelation::cleanup()
|
||||
{
|
||||
QLocale::setDefault( QLocale::English );
|
||||
}
|
||||
|
||||
void TestQgsWeakRelation::testResolved()
|
||||
{
|
||||
QList<QgsRelation::FieldPair> fieldPairs {{ "fk_province", "pk" }};
|
||||
|
||||
QgsWeakRelation weakRel( QStringLiteral( "my_relation_id" ),
|
||||
QStringLiteral( "my_relation_name" ),
|
||||
QgsRelation::RelationStrength::Association,
|
||||
QStringLiteral( "referencingLayerId" ),
|
||||
QStringLiteral( "referencingLayerName" ),
|
||||
QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=fk_province:int&field=fk_municipality:int" ),
|
||||
QStringLiteral( "memory" ),
|
||||
QStringLiteral( "referencedLayerId" ),
|
||||
QStringLiteral( "referencedLayerName" ),
|
||||
QStringLiteral( "Polygon?crs=epsg:4326&field=pk:int&field=province:int&field=municipality:string" ),
|
||||
QStringLiteral( "memory" ),
|
||||
fieldPairs
|
||||
);
|
||||
QVERIFY( ! weakRel.resolvedRelation( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ).isValid() );
|
||||
|
||||
// create a vector layer
|
||||
QgsVectorLayer referencedLayer( QStringLiteral( "Polygon?crs=epsg:4326&field=pk:int&field=province:int&field=municipality:string" ), QStringLiteral( "referencedLayerName" ), QStringLiteral( "memory" ) );
|
||||
QgsProject::instance()->addMapLayer( &referencedLayer, false, false );
|
||||
QVERIFY( ! weakRel.resolvedRelation( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ).isValid() );
|
||||
|
||||
QgsVectorLayer referencingLayer( QStringLiteral( "Point?crs=epsg:4326&field=pk:int&field=fk_province:int&field=fk_municipality:int" ), QStringLiteral( "referencingLayerName" ), QStringLiteral( "memory" ) );
|
||||
QgsProject::instance()->addMapLayer( &referencingLayer, false, false );
|
||||
QVERIFY( weakRel.resolvedRelation( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ).isValid() );
|
||||
|
||||
QVERIFY( weakRel.resolvedRelation( QgsProject::instance(), static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Name | QgsVectorLayerRef::MatchType::Provider ) ).isValid() );
|
||||
|
||||
// This fails because memory provider stores an UUID in the data source definition ...
|
||||
QVERIFY( !weakRel.resolvedRelation( QgsProject::instance(), static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Name | QgsVectorLayerRef::MatchType::Source ) ).isValid() );
|
||||
|
||||
// ... let's fix it
|
||||
weakRel.mReferencedLayer.source = referencedLayer.publicSource();
|
||||
weakRel.mReferencingLayer.source = referencingLayer.publicSource();
|
||||
QVERIFY( weakRel.resolvedRelation( QgsProject::instance(), static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Name | QgsVectorLayerRef::MatchType::Source ) ).isValid() );
|
||||
|
||||
// Just to be sure
|
||||
QVERIFY( weakRel.resolvedRelation( QgsProject::instance() ).isValid() );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsWeakRelation )
|
||||
#include "testqgsweakrelation.moc"
|
Loading…
x
Reference in New Issue
Block a user