mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
Merge pull request #32487 from elpaso/value-relation-restore
[feature] Value relation restore missing layers from DBs
This commit is contained in:
commit
696c4075da
@ -1538,6 +1538,15 @@ Emitted whenever the layer's data source has been changed.
|
||||
.. versionadded:: 3.5
|
||||
%End
|
||||
|
||||
void styleLoaded( QgsMapLayer::StyleCategories categories );
|
||||
%Docstring
|
||||
Emitted when a style has been loaded
|
||||
|
||||
:param categories: style categories
|
||||
|
||||
.. versionadded:: 3.12
|
||||
%End
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -187,6 +187,7 @@ of attribute values failing enforced field constraints.
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
|
||||
QString constraintFailureReason() const;
|
||||
%Docstring
|
||||
Returns the reason why a constraint check has failed (or an empty string
|
||||
|
@ -84,6 +84,7 @@
|
||||
#include "qgsgeometryvalidationservice.h"
|
||||
#include "qgssourceselectproviderregistry.h"
|
||||
#include "qgssourceselectprovider.h"
|
||||
#include "qgsprovidermetadata.h"
|
||||
|
||||
#include "qgsanalysis.h"
|
||||
#include "qgsgeometrycheckregistry.h"
|
||||
@ -661,6 +662,18 @@ void QgisApp::onActiveLayerChanged( QgsMapLayer *layer )
|
||||
emit activeLayerChanged( layer );
|
||||
}
|
||||
|
||||
void QgisApp::vectorLayerStyleLoaded( QgsMapLayer::StyleCategories categories )
|
||||
{
|
||||
if ( categories.testFlag( QgsMapLayer::StyleCategory::Forms ) )
|
||||
{
|
||||
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( sender() );
|
||||
if ( vl && vl->isValid( ) )
|
||||
{
|
||||
checkVectorLayerDependencies( vl );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function contains forced validation of CRS used in QGIS.
|
||||
* There are 4 options depending on the settings:
|
||||
@ -1942,6 +1955,131 @@ QgsMessageBar *QgisApp::visibleMessageBar()
|
||||
}
|
||||
}
|
||||
|
||||
QList<QgsVectorLayerRef> QgisApp::findBrokenWidgetDependencies( QgsVectorLayer *vl )
|
||||
{
|
||||
QList<QgsVectorLayerRef> brokenDependencies;
|
||||
// Check for missing layer widget dependencies
|
||||
for ( int i = 0; i < vl->fields().count(); i++ )
|
||||
{
|
||||
std::unique_ptr<QgsEditorWidgetWrapper> ww;
|
||||
ww.reset( QgsGui::editorWidgetRegistry()->create( vl, i, nullptr, nullptr ) );
|
||||
// ww should never be null in real life, but it is in QgisApp tests because
|
||||
// QgsEditorWidgetRegistry widget factories is empty
|
||||
if ( ww )
|
||||
{
|
||||
const auto constDependencies { ww->layerDependencies() };
|
||||
for ( const QgsVectorLayerRef &dependency : constDependencies )
|
||||
{
|
||||
const QgsVectorLayer *depVl { QgsVectorLayerRef( dependency ).resolveWeakly(
|
||||
QgsProject::instance(),
|
||||
QgsVectorLayerRef::MatchType::Name ) };
|
||||
if ( ! depVl || ! depVl->isValid() )
|
||||
{
|
||||
brokenDependencies.append( dependency );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return brokenDependencies;
|
||||
}
|
||||
|
||||
void QgisApp::checkVectorLayerDependencies( QgsVectorLayer *vl )
|
||||
{
|
||||
if ( vl && vl->isValid() )
|
||||
{
|
||||
const auto constDependencies { findBrokenWidgetDependencies( vl ) };
|
||||
for ( const QgsVectorLayerRef &dependency : constDependencies )
|
||||
{
|
||||
if ( ! vl || ! vl->isValid() )
|
||||
{
|
||||
// try to aggressively resolve the broken dependencies
|
||||
bool loaded = false;
|
||||
const QString providerName { vl->dataProvider()->name() };
|
||||
QgsProviderMetadata *providerMetadata { QgsProviderRegistry::instance()->providerMetadata( providerName ) };
|
||||
if ( providerMetadata )
|
||||
{
|
||||
// Retrieve the DB connection (if any)
|
||||
std::unique_ptr< QgsAbstractDatabaseProviderConnection > conn { static_cast<QgsAbstractDatabaseProviderConnection *>( providerMetadata->createConnection( vl->dataProvider()->uri().uri(), {} ) ) };
|
||||
if ( conn )
|
||||
{
|
||||
QString tableSchema;
|
||||
QString tableName;
|
||||
const QVariantMap sourceParts { providerMetadata->decodeUri( dependency.source ) };
|
||||
|
||||
// This part should really be abstracted out to the connection classes or to the providers directly.
|
||||
// Different providers decode the uri differently, for example we don't get the table name out of OGR
|
||||
// but the layerName/layerId instead, so let's try different approaches
|
||||
|
||||
// This works for GPKG
|
||||
tableName = sourceParts.value( QStringLiteral( "layerName" ) ).toString();
|
||||
|
||||
// This works for PG and spatialite
|
||||
if ( tableName.isEmpty() )
|
||||
{
|
||||
tableName = sourceParts.value( QStringLiteral( "table" ) ).toString();
|
||||
tableSchema = sourceParts.value( QStringLiteral( "schema" ) ).toString();
|
||||
}
|
||||
|
||||
// Helper to find layers in connections
|
||||
auto layerFinder = [ &conn, &dependency, &providerName ]( const QString & tableSchema, const QString & tableName ) -> bool
|
||||
{
|
||||
// First try the current schema (or no schema if it's not supported from the provider)
|
||||
try
|
||||
{
|
||||
const QString layerUri { conn->tableUri( tableSchema, tableName )};
|
||||
// Load it!
|
||||
std::unique_ptr< QgsVectorLayer > newVl = qgis::make_unique< QgsVectorLayer >( layerUri, dependency.name, providerName );
|
||||
if ( newVl->isValid() )
|
||||
{
|
||||
QgsProject::instance()->addMapLayer( newVl.release() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch ( QgsProviderConnectionException & )
|
||||
{
|
||||
// Do nothing!
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
loaded = layerFinder( tableSchema, tableName );
|
||||
|
||||
// Try different schemas
|
||||
if ( ! loaded && conn->capabilities().testFlag( QgsAbstractDatabaseProviderConnection::Capability::Schemas ) && ! tableSchema.isEmpty() )
|
||||
{
|
||||
const QStringList schemas { conn->schemas() };
|
||||
for ( const QString &schemaName : schemas )
|
||||
{
|
||||
if ( schemaName != tableSchema )
|
||||
{
|
||||
loaded = layerFinder( schemaName, tableName );
|
||||
}
|
||||
if ( loaded )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! loaded )
|
||||
{
|
||||
const QString msg { tr( "layer '%1' requires layer '%2' to be loaded but '%2' could not be found, please load it manually if possible." )
|
||||
.arg( vl->name() )
|
||||
.arg( dependency.name ) };
|
||||
messageBar()->pushWarning( tr( "Missing layer form dependency" ), msg );
|
||||
}
|
||||
else
|
||||
{
|
||||
messageBar()->pushSuccess( tr( "Missing layer form dependency" ), tr( "Layer dependency '%2' required by '%1' was automatically loaded." )
|
||||
.arg( vl->name() )
|
||||
.arg( dependency.name ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgisApp::dataSourceManager( const QString &pageName )
|
||||
{
|
||||
if ( ! mDataSourceManagerDialog )
|
||||
@ -2809,7 +2947,6 @@ void QgisApp::createToolBars()
|
||||
case 3:
|
||||
defSelectionAction = mActionInvertSelection;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
bt->setDefaultAction( defSelectionAction );
|
||||
QAction *selectionAction = mAttributesToolBar->insertWidget( mActionDeselectAll, bt );
|
||||
@ -2968,7 +3105,7 @@ void QgisApp::createToolBars()
|
||||
case 1:
|
||||
defMapServiceAction = mActionAddAmsLayer;
|
||||
break;
|
||||
};
|
||||
}
|
||||
bt->setDefaultAction( defMapServiceAction );
|
||||
QAction *mapServiceAction = mLayerToolBar->insertWidget( mActionAddWmsLayer, bt );
|
||||
mLayerToolBar->removeAction( mActionAddWmsLayer );
|
||||
@ -2989,7 +3126,7 @@ void QgisApp::createToolBars()
|
||||
case 1:
|
||||
defFeatureServiceAction = mActionAddAfsLayer;
|
||||
break;
|
||||
};
|
||||
}
|
||||
bt->setDefaultAction( defFeatureServiceAction );
|
||||
QAction *featureServiceAction = mLayerToolBar->insertWidget( mActionAddWfsLayer, bt );
|
||||
mLayerToolBar->removeAction( mActionAddWfsLayer );
|
||||
@ -3049,7 +3186,7 @@ void QgisApp::createToolBars()
|
||||
case 1:
|
||||
defActionCircularString = mActionCircularStringRadius;
|
||||
break;
|
||||
};
|
||||
}
|
||||
tbAddCircularString->setDefaultAction( defActionCircularString );
|
||||
QAction *addCircularAction = mShapeDigitizeToolBar->insertWidget( mActionVertexTool, tbAddCircularString );
|
||||
addCircularAction->setObjectName( QStringLiteral( "ActionAddCircularString" ) );
|
||||
@ -3081,7 +3218,7 @@ void QgisApp::createToolBars()
|
||||
case 4:
|
||||
defActionCircle = mActionCircleCenterPoint;
|
||||
break;
|
||||
};
|
||||
}
|
||||
tbAddCircle->setDefaultAction( defActionCircle );
|
||||
QAction *addCircleAction = mShapeDigitizeToolBar->insertWidget( mActionVertexTool, tbAddCircle );
|
||||
addCircleAction->setObjectName( QStringLiteral( "ActionAddCircle" ) );
|
||||
@ -3109,7 +3246,7 @@ void QgisApp::createToolBars()
|
||||
case 3:
|
||||
defActionEllipse = mActionEllipseFoci;
|
||||
break;
|
||||
};
|
||||
}
|
||||
tbAddEllipse->setDefaultAction( defActionEllipse );
|
||||
QAction *addEllipseAction = mShapeDigitizeToolBar->insertWidget( mActionVertexTool, tbAddEllipse );
|
||||
addEllipseAction->setObjectName( QStringLiteral( "ActionAddEllipse" ) );
|
||||
@ -3137,7 +3274,7 @@ void QgisApp::createToolBars()
|
||||
case 3:
|
||||
defActionRectangle = mActionRectangle3PointsProjected;
|
||||
break;
|
||||
};
|
||||
}
|
||||
tbAddRectangle->setDefaultAction( defActionRectangle );
|
||||
QAction *addRectangleAction = mShapeDigitizeToolBar->insertWidget( mActionVertexTool, tbAddRectangle );
|
||||
addRectangleAction->setObjectName( QStringLiteral( "ActionAddRectangle" ) );
|
||||
@ -3161,7 +3298,7 @@ void QgisApp::createToolBars()
|
||||
case 2:
|
||||
defActionRegularPolygon = mActionRegularPolygonCenterCorner;
|
||||
break;
|
||||
};
|
||||
}
|
||||
tbAddRegularPolygon->setDefaultAction( defActionRegularPolygon );
|
||||
QAction *addRegularPolygonAction = mShapeDigitizeToolBar->insertWidget( mActionVertexTool, tbAddRegularPolygon );
|
||||
addRegularPolygonAction->setObjectName( QStringLiteral( "ActionAddRegularPolygon" ) );
|
||||
@ -3184,7 +3321,7 @@ void QgisApp::createToolBars()
|
||||
case 1:
|
||||
defAction = mActionMoveFeatureCopy;
|
||||
break;
|
||||
};
|
||||
}
|
||||
moveFeatureButton->setDefaultAction( defAction );
|
||||
QAction *moveToolAction = mAdvancedDigitizeToolBar->insertWidget( mActionRotateFeature, moveFeatureButton );
|
||||
moveToolAction->setObjectName( QStringLiteral( "ActionMoveFeatureTool" ) );
|
||||
@ -3204,7 +3341,7 @@ void QgisApp::createToolBars()
|
||||
case QgsVertexTool::ActiveLayer:
|
||||
defActionVertexTool = mActionVertexToolActiveLayer;
|
||||
break;
|
||||
};
|
||||
}
|
||||
vertexToolButton->setDefaultAction( defActionVertexTool );
|
||||
connect( vertexToolButton, &QToolButton::triggered, this, &QgisApp::toolButtonActionTriggered );
|
||||
|
||||
@ -6317,6 +6454,16 @@ bool QgisApp::addProject( const QString &projectFile )
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check for missing layer widget dependencies
|
||||
const auto constVLayers { QgsProject::instance()->layers<QgsVectorLayer *>( ) };
|
||||
for ( QgsVectorLayer *vl : constVLayers )
|
||||
{
|
||||
if ( vl->isValid() )
|
||||
{
|
||||
checkVectorLayerDependencies( vl );
|
||||
}
|
||||
}
|
||||
|
||||
emit projectRead(); // let plug-ins know that we've read in a new
|
||||
// project so that they can check any project
|
||||
// specific plug-in state
|
||||
@ -12570,6 +12717,7 @@ void QgisApp::layersWereAdded( const QList<QgsMapLayer *> &layers )
|
||||
connect( vlayer, &QgsVectorLayer::editingStopped, this, &QgisApp::layerEditStateChanged );
|
||||
connect( vlayer, &QgsVectorLayer::readOnlyChanged, this, &QgisApp::layerEditStateChanged );
|
||||
connect( vlayer, &QgsVectorLayer::raiseError, this, &QgisApp::onLayerError );
|
||||
connect( vlayer, &QgsVectorLayer::styleLoaded, this, &QgisApp::vectorLayerStyleLoaded );
|
||||
|
||||
provider = vProvider;
|
||||
}
|
||||
|
@ -161,6 +161,7 @@ class QgsNetworkRequestParameters;
|
||||
#include "ogr/qgsvectorlayersaveasdialog.h"
|
||||
#include "ui_qgisapp.h"
|
||||
#include "qgis_app.h"
|
||||
#include "qgsvectorlayerref.h"
|
||||
|
||||
#include <QGestureEvent>
|
||||
#include <QTapAndHoldGesture>
|
||||
@ -1700,6 +1701,13 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
|
||||
void onActiveLayerChanged( QgsMapLayer *layer );
|
||||
|
||||
/**
|
||||
* Triggered when a vector layer style has changed, checks for widget config layer dependencies
|
||||
* \param categories style categories
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
void vectorLayerStyleLoaded( const QgsMapLayer::StyleCategories categories );
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
@ -2008,8 +2016,20 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
//! Returns the message bar of the datasource manager dialog if it is visible, the canvas's message bar otherwise.
|
||||
QgsMessageBar *visibleMessageBar();
|
||||
|
||||
/**
|
||||
* Searches for layer widget dependencies
|
||||
* \return a list of weak references to broken widget layer dependencies
|
||||
*/
|
||||
QList< QgsVectorLayerRef > findBrokenWidgetDependencies( QgsVectorLayer *vectorLayer );
|
||||
|
||||
/**
|
||||
* Scans the \a vectorLayer for broken dependencies and warns the user
|
||||
*/
|
||||
void checkVectorLayerDependencies( QgsVectorLayer *vectorLayer );
|
||||
|
||||
QgisAppStyleSheet *mStyleSheetBuilder = nullptr;
|
||||
|
||||
|
||||
// actions for menus and toolbars -----------------
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
|
@ -1370,6 +1370,13 @@ class CORE_EXPORT QgsMapLayer : public QObject
|
||||
*/
|
||||
void dataSourceChanged();
|
||||
|
||||
/**
|
||||
* Emitted when a style has been loaded
|
||||
* \param categories style categories
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
void styleLoaded( QgsMapLayer::StyleCategories categories );
|
||||
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -33,6 +33,19 @@ template<typename TYPE>
|
||||
struct _LayerRef
|
||||
{
|
||||
|
||||
/**
|
||||
* Flag for match type in weak resolution
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
enum MatchType
|
||||
{
|
||||
Name = 1 << 2, //! Match layer name
|
||||
Provider = 1 << 3, //! Match layer provider name
|
||||
Source = 1 << 4, //! Match layer source
|
||||
All = Provider | Source //!< Match all
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for a layer reference from an existing map layer.
|
||||
* The layerId, source, name and provider members will automatically
|
||||
@ -146,6 +159,30 @@ struct _LayerRef
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool layerMatchesWeakly( QgsMapLayer *layer, MatchType matchType = MatchType::All )
|
||||
{
|
||||
// First match the name
|
||||
if ( matchType & MatchType::Name && ( layer->name().isEmpty() || layer->name() != name ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have found a match by name, now check the other
|
||||
// criteria
|
||||
if ( matchType & MatchType::Provider && layer->providerType() != provider )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if ( matchType & MatchType::Source && layer->publicSource() != source )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// All tests passed
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the map layer by attempting to find a matching layer
|
||||
* in a \a project using a weak match.
|
||||
@ -157,27 +194,46 @@ struct _LayerRef
|
||||
* the weak references to layer public source, layer name and data
|
||||
* provider contained in this layer reference.
|
||||
*
|
||||
* The \a matchType enum can used to refine the matching criteria
|
||||
* when using the weak reference and include layer name,
|
||||
* provider name and layer source in the equality test,
|
||||
* by default they are all checked.
|
||||
*
|
||||
* If a matching layer is found, this reference will be updated to match
|
||||
* the found layer and the layer will be returned. If no matching layer is
|
||||
* found, NULLPTR is returned.
|
||||
* \see resolve()
|
||||
* \see layerMatchesSource()
|
||||
* \see layerMatchesWeakly()
|
||||
* \see resolveByIdOrNameOnly()
|
||||
*/
|
||||
TYPE *resolveWeakly( const QgsProject *project )
|
||||
TYPE *resolveWeakly( const QgsProject *project, MatchType matchType = MatchType::All )
|
||||
{
|
||||
// first try matching by layer ID
|
||||
if ( resolve( project ) )
|
||||
return layer;
|
||||
|
||||
if ( project && !name.isEmpty() )
|
||||
if ( project )
|
||||
{
|
||||
const QList<QgsMapLayer *> layers = project->mapLayersByName( name );
|
||||
for ( QgsMapLayer *l : layers )
|
||||
QList<QgsMapLayer *> layers;
|
||||
// If matching by name ...
|
||||
if ( matchType & MatchType::Name )
|
||||
{
|
||||
if ( name.isEmpty() )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
layers = project->mapLayersByName( name );
|
||||
}
|
||||
else // ... we need all layers
|
||||
{
|
||||
layers = project->mapLayers().values();
|
||||
}
|
||||
for ( QgsMapLayer *l : qgis::as_const( layers ) )
|
||||
{
|
||||
if ( TYPE *tl = qobject_cast< TYPE *>( l ) )
|
||||
{
|
||||
if ( layerMatchesSource( tl ) )
|
||||
if ( layerMatchesWeakly( tl, matchType ) )
|
||||
{
|
||||
setLayer( tl );
|
||||
return tl;
|
||||
|
@ -4676,6 +4676,7 @@ QgsAuxiliaryLayer *QgsVectorLayer::auxiliaryLayer()
|
||||
QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories )
|
||||
{
|
||||
QgsDataSourceUri dsUri( theURI );
|
||||
QString returnMessage;
|
||||
if ( !loadFromLocalDB && mDataProvider && mDataProvider->isSaveAndLoadStyleToDatabaseSupported() )
|
||||
{
|
||||
QString qml, errorMsg;
|
||||
@ -4685,10 +4686,12 @@ QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &resultFlag,
|
||||
QDomDocument myDocument( QStringLiteral( "qgis" ) );
|
||||
myDocument.setContent( qml );
|
||||
resultFlag = importNamedStyle( myDocument, errorMsg );
|
||||
return QObject::tr( "Loaded from Provider" );
|
||||
returnMessage = QObject::tr( "Loaded from Provider" );
|
||||
}
|
||||
}
|
||||
return QgsMapLayer::loadNamedStyle( theURI, resultFlag, categories );
|
||||
returnMessage = QgsMapLayer::loadNamedStyle( theURI, resultFlag, categories );
|
||||
emit styleLoaded( categories );
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
QSet<QgsMapLayerDependency> QgsVectorLayer::dependencies() const
|
||||
|
@ -80,7 +80,7 @@ QgsEditorWidgetSetup QgsEditorWidgetRegistry::findBest( const QgsVectorLayer *vl
|
||||
|
||||
if ( index > -1 )
|
||||
{
|
||||
QgsEditorWidgetSetup setup = vl->fields().at( index ).editorWidgetSetup();
|
||||
QgsEditorWidgetSetup setup = fields.at( index ).editorWidgetSetup();
|
||||
if ( !setup.isNull() )
|
||||
return setup;
|
||||
}
|
||||
|
@ -267,6 +267,11 @@ bool QgsEditorWidgetWrapper::isBlockingCommit() const
|
||||
return mIsBlockingCommit;
|
||||
}
|
||||
|
||||
QList<QgsVectorLayerRef> QgsEditorWidgetWrapper::layerDependencies() const
|
||||
{
|
||||
return QList<QgsVectorLayerRef>();
|
||||
}
|
||||
|
||||
QString QgsEditorWidgetWrapper::constraintFailureReason() const
|
||||
{
|
||||
return mConstraintFailureReason;
|
||||
|
@ -27,6 +27,7 @@ class QgsField;
|
||||
#include "qgswidgetwrapper.h"
|
||||
#include "qgis_gui.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsvectorlayerref.h"
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
@ -192,6 +193,19 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
|
||||
*/
|
||||
bool isBlockingCommit() const;
|
||||
|
||||
/**
|
||||
* Returns a list of weak layer references to other layers required by this widget.
|
||||
* The default implementation returns an empty list.
|
||||
*
|
||||
* This method should be reimplemented by widgets that handle relations with other layers,
|
||||
* (e.g. ValueRelation) and can be used by client code to warn the user about
|
||||
* missing required dependencies or to add some resolution logic in order
|
||||
* to load the missing dependency.
|
||||
* \note not available in Python bindings
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
virtual QList< QgsVectorLayerRef > layerDependencies() const SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns the reason why a constraint check has failed (or an empty string
|
||||
* if constraint check was successful).
|
||||
|
@ -457,3 +457,18 @@ void QgsValueRelationWidgetWrapper::setEnabled( bool enabled )
|
||||
else
|
||||
QgsEditorWidgetWrapper::setEnabled( enabled );
|
||||
}
|
||||
|
||||
|
||||
QList<QgsVectorLayerRef> QgsValueRelationWidgetWrapper::layerDependencies() const
|
||||
{
|
||||
QList<QgsVectorLayerRef> result;
|
||||
const QString layerId { config().value( QStringLiteral( "Layer" ) ).toString() };
|
||||
const QString layerName { config().value( QStringLiteral( "LayerName" ) ).toString() };
|
||||
const QString providerName { config().value( QStringLiteral( "LayerProviderName" ) ).toString() };
|
||||
const QString layerSource { config().value( QStringLiteral( "LayerSource" ) ).toString() };
|
||||
if ( ! layerId.isEmpty() && ! layerName.isEmpty() && ! providerName.isEmpty() && ! layerSource.isEmpty() )
|
||||
{
|
||||
result.append( QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ class GUI_EXPORT QgsValueRelationWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
*/
|
||||
void setFeature( const QgsFeature &feature ) override;
|
||||
|
||||
QList<QgsVectorLayerRef> layerDependencies() const override;
|
||||
|
||||
private:
|
||||
void updateValues( const QVariant &value, const QVariantList & = QVariantList() ) override;
|
||||
@ -131,6 +132,7 @@ class GUI_EXPORT QgsValueRelationWidgetWrapper : public QgsEditorWidgetWrapper
|
||||
|
||||
friend class QgsValueRelationWidgetFactory;
|
||||
friend class TestQgsValueRelationWidgetWrapper;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSVALUERELATIONWIDGETWRAPPER_H
|
||||
|
@ -5260,3 +5260,29 @@ QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
|
||||
return new QgsPostgresProviderMetadata();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
QVariantMap QgsPostgresProviderMetadata::decodeUri( const QString &uri )
|
||||
{
|
||||
const QgsDataSourceUri dsUri { uri };
|
||||
return
|
||||
{
|
||||
{ QStringLiteral( "dbname" ), dsUri.database() },
|
||||
{ QStringLiteral( "host" ), dsUri.host() },
|
||||
{ QStringLiteral( "port" ), dsUri.port() },
|
||||
{ QStringLiteral( "service" ), dsUri.service() },
|
||||
{ QStringLiteral( "username" ), dsUri.username() },
|
||||
{ QStringLiteral( "password" ), dsUri.password() },
|
||||
{ QStringLiteral( "authcfg" ), dsUri.authConfigId() },
|
||||
{ QStringLiteral( "type" ), dsUri.wkbType() },
|
||||
{ QStringLiteral( "selectatid" ), dsUri.selectAtIdDisabled() },
|
||||
{ QStringLiteral( "table" ), dsUri.table() },
|
||||
{ QStringLiteral( "schema" ), dsUri.schema() },
|
||||
{ QStringLiteral( "key" ), dsUri.keyColumn() },
|
||||
{ QStringLiteral( "srid" ), dsUri.srid() },
|
||||
{ QStringLiteral( "estimatedmetadata" ), dsUri.useEstimatedMetadata() },
|
||||
{ QStringLiteral( "sslmode" ), dsUri.sslMode() },
|
||||
{ QStringLiteral( "sql" ), dsUri.sql() },
|
||||
{ QStringLiteral( "geometrycolumn" ), dsUri.geometryColumn() },
|
||||
};
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ class QgsPostgresProviderMetadata: public QgsProviderMetadata
|
||||
void saveConnection( const QgsAbstractProviderConnection *createConnection, const QString &name ) override;
|
||||
void initProvider() override;
|
||||
void cleanupProvider() override;
|
||||
|
||||
QVariantMap decodeUri( const QString &uri ) override;
|
||||
};
|
||||
|
||||
// clazy:excludeall=qstring-allocations
|
||||
|
@ -73,6 +73,7 @@ class TestQgsMapLayer : public QObject
|
||||
void layerRef();
|
||||
void layerRefListUtils();
|
||||
void layerRefResolveByIdOrNameOnly();
|
||||
void layerRefResolveWeakly();
|
||||
|
||||
void styleCategories();
|
||||
|
||||
@ -307,6 +308,49 @@ void TestQgsMapLayer::layerRefResolveByIdOrNameOnly()
|
||||
QgsProject::instance()->removeAllMapLayers();
|
||||
}
|
||||
|
||||
void TestQgsMapLayer::layerRefResolveWeakly()
|
||||
{
|
||||
QgsVectorLayer *vlA = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "name" ), QStringLiteral( "memory" ) );
|
||||
QgsVectorLayerRef ref;
|
||||
QgsProject::instance()->addMapLayer( vlA );
|
||||
ref.name = vlA->name();
|
||||
QVERIFY( ! ref.resolveWeakly( QgsProject::instance() ) );
|
||||
QVERIFY( ref.resolveWeakly( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ) );
|
||||
|
||||
ref = QgsVectorLayerRef();
|
||||
ref.name = QStringLiteral( "another name" );
|
||||
QVERIFY( ! ref.resolveWeakly( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ) );
|
||||
ref.provider = vlA->providerType();
|
||||
QVERIFY( ref.resolveWeakly( QgsProject::instance(), QgsVectorLayerRef::MatchType::Provider ) );
|
||||
|
||||
ref = QgsVectorLayerRef();
|
||||
ref.name = QStringLiteral( "another name" );
|
||||
QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
|
||||
static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Provider |
|
||||
QgsVectorLayerRef::MatchType::Name ) ) );
|
||||
ref.provider = vlA->providerType();
|
||||
QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
|
||||
static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Provider |
|
||||
QgsVectorLayerRef::MatchType::Name ) ) );
|
||||
ref.name = vlA->name();
|
||||
QVERIFY( ref.resolveWeakly( QgsProject::instance(),
|
||||
static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Provider |
|
||||
QgsVectorLayerRef::MatchType::Name ) ) );
|
||||
|
||||
ref = QgsVectorLayerRef();
|
||||
QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
|
||||
static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Source |
|
||||
QgsVectorLayerRef::MatchType::Name ) ) );
|
||||
ref.source = vlA->publicSource();
|
||||
QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
|
||||
static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Source |
|
||||
QgsVectorLayerRef::MatchType::Name ) ) );
|
||||
ref.name = vlA->name();
|
||||
QVERIFY( ref.resolveWeakly( QgsProject::instance(),
|
||||
static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Source |
|
||||
QgsVectorLayerRef::MatchType::Name ) ) );
|
||||
}
|
||||
|
||||
void TestQgsMapLayer::styleCategories()
|
||||
{
|
||||
// control that AllStyleCategories is actually complete
|
||||
|
Loading…
x
Reference in New Issue
Block a user