Merge pull request #32487 from elpaso/value-relation-restore

[feature] Value relation restore missing layers from DBs
This commit is contained in:
Alessandro Pasotti 2019-11-04 12:03:40 +01:00 committed by GitHub
commit 696c4075da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 369 additions and 19 deletions

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -267,6 +267,11 @@ bool QgsEditorWidgetWrapper::isBlockingCommit() const
return mIsBlockingCommit;
}
QList<QgsVectorLayerRef> QgsEditorWidgetWrapper::layerDependencies() const
{
return QList<QgsVectorLayerRef>();
}
QString QgsEditorWidgetWrapper::constraintFailureReason() const
{
return mConstraintFailureReason;

View File

@ -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).

View File

@ -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;
}

View File

@ -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

View File

@ -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() },
};
}

View File

@ -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

View File

@ -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