QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgslayerdefinition.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayerdefinition.cpp
3  ---------------------
4  begin : January 2015
5  copyright : (C) 2015 by Nathan Woodrow
6  email : woodrow dot nathan at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <QFileInfo>
16 #include <QFile>
17 #include <QDir>
18 #include <QTextStream>
19 
20 #include "qgslayerdefinition.h"
21 #include "qgslayertree.h"
22 #include "qgslogger.h"
23 #include "qgsmaplayer.h"
24 #include "qgspathresolver.h"
25 #include "qgspluginlayer.h"
26 #include "qgspluginlayerregistry.h"
27 #include "qgsproject.h"
28 #include "qgsrasterlayer.h"
29 #include "qgsreadwritecontext.h"
30 #include "qgsvectorlayer.h"
31 #include "qgsvectortilelayer.h"
32 #include "qgsapplication.h"
33 
34 bool QgsLayerDefinition::loadLayerDefinition( const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage )
35 {
36  QFile file( path );
37  if ( !file.open( QIODevice::ReadOnly ) )
38  {
39  errorMessage = QStringLiteral( "Can not open file" );
40  return false;
41  }
42 
43  QDomDocument doc;
44  QString message;
45  if ( !doc.setContent( &file, &message ) )
46  {
47  errorMessage = message;
48  return false;
49  }
50 
51  QFileInfo fileinfo( file );
52  QDir::setCurrent( fileinfo.absoluteDir().path() );
53 
54  QgsReadWriteContext context;
55  context.setPathResolver( QgsPathResolver( path ) );
56  context.setProjectTranslator( project );
57 
58  return loadLayerDefinition( doc, project, rootGroup, errorMessage, context );
59 }
60 
61 bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage, QgsReadWriteContext &context )
62 {
63  Q_UNUSED( errorMessage )
64 
66 
67  // reorder maplayer nodes based on dependencies
68  // dependencies have to be resolved before IDs get changed
69  DependencySorter depSorter( doc );
70  if ( !depSorter.hasMissingDependency() )
71  {
72  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
73  QVector<QDomNode> clonedSorted;
74  const auto constSortedLayerNodes = sortedLayerNodes;
75  for ( const QDomNode &node : constSortedLayerNodes )
76  {
77  clonedSorted << node.cloneNode();
78  }
79  QDomNode layersNode = doc.elementsByTagName( QStringLiteral( "maplayers" ) ).at( 0 );
80  // replace old children with new ones
81  QDomNodeList childNodes = layersNode.childNodes();
82  for ( int i = 0; i < childNodes.size(); i++ )
83  {
84  layersNode.replaceChild( clonedSorted.at( i ), childNodes.at( i ) );
85  }
86  }
87  // if a dependency is missing, we still try to load layers, since dependencies may already be loaded
88 
89  // IDs of layers should be changed otherwise we may have more then one layer with the same id
90  // We have to replace the IDs before we load them because it's too late once they are loaded
91  QDomNodeList treeLayerNodes = doc.elementsByTagName( QStringLiteral( "layer-tree-layer" ) );
92  for ( int i = 0; i < treeLayerNodes.size(); ++i )
93  {
94  QDomNode treeLayerNode = treeLayerNodes.at( i );
95  QDomElement treeLayerElem = treeLayerNode.toElement();
96  QString oldid = treeLayerElem.attribute( QStringLiteral( "id" ) );
97  QString layername = treeLayerElem.attribute( QStringLiteral( "name" ) );
98  QString newid = QgsMapLayer::generateId( layername );
99  treeLayerElem.setAttribute( QStringLiteral( "id" ), newid );
100 
101  // Replace IDs for map layers
102  QDomNodeList ids = doc.elementsByTagName( QStringLiteral( "id" ) );
103  for ( int i = 0; i < ids.size(); ++i )
104  {
105  QDomNode idnode = ids.at( i );
106  QDomElement idElem = idnode.toElement();
107  if ( idElem.text() == oldid )
108  {
109  idElem.firstChild().setNodeValue( newid );
110  }
111  }
112 
113  // change layer IDs for vector joins
114  QDomNodeList vectorJoinNodes = doc.elementsByTagName( QStringLiteral( "join" ) ); // TODO: Find a better way of searching for vectorjoins, there might be other <join> elements within the project.
115  for ( int j = 0; j < vectorJoinNodes.size(); ++j )
116  {
117  QDomNode joinNode = vectorJoinNodes.at( j );
118  QDomElement joinElement = joinNode.toElement();
119  if ( joinElement.attribute( QStringLiteral( "joinLayerId" ) ) == oldid )
120  {
121  joinNode.toElement().setAttribute( QStringLiteral( "joinLayerId" ), newid );
122  }
123  }
124 
125  // change IDs of dependencies
126  QDomNodeList dataDeps = doc.elementsByTagName( QStringLiteral( "dataDependencies" ) );
127  for ( int i = 0; i < dataDeps.size(); i++ )
128  {
129  QDomNodeList layers = dataDeps.at( i ).childNodes();
130  for ( int j = 0; j < layers.size(); j++ )
131  {
132  QDomElement elt = layers.at( j ).toElement();
133  if ( elt.attribute( QStringLiteral( "id" ) ) == oldid )
134  {
135  elt.setAttribute( QStringLiteral( "id" ), newid );
136  }
137  }
138  }
139 
140  // Change IDs of widget config values
141  QDomNodeList widgetConfig = doc.elementsByTagName( QStringLiteral( "editWidget" ) );
142  for ( int i = 0; i < widgetConfig.size(); i++ )
143  {
144  QDomNodeList config = widgetConfig.at( i ).childNodes();
145  for ( int j = 0; j < config.size(); j++ )
146  {
147  QDomNodeList optMap = config.at( j ).childNodes();
148  for ( int z = 0; z < optMap.size(); z++ )
149  {
150  QDomNodeList opts = optMap.at( z ).childNodes();
151  for ( int k = 0; k < opts.size(); k++ )
152  {
153  QDomElement opt = opts.at( k ).toElement();
154  if ( opt.attribute( QStringLiteral( "value" ) ) == oldid )
155  {
156  opt.setAttribute( QStringLiteral( "value" ), newid );
157  }
158  }
159  }
160  }
161  }
162  }
163 
164  QDomElement layerTreeElem = doc.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
165  bool loadInLegend = true;
166  if ( !layerTreeElem.isNull() )
167  {
168  root->readChildrenFromXml( layerTreeElem, context );
169  loadInLegend = false;
170  }
171 
172  QList<QgsMapLayer *> layers = QgsLayerDefinition::loadLayerDefinitionLayers( doc, context );
173 
174  project->addMapLayers( layers, loadInLegend );
175 
176  // Now that all layers are loaded, refresh the vectorjoins to get the joined fields
177  const auto constLayers = layers;
178  for ( QgsMapLayer *layer : constLayers )
179  {
180  if ( QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( layer ) )
181  {
182  vlayer->resolveReferences( project );
183  }
184  }
185 
186  root->resolveReferences( project );
187 
188  QList<QgsLayerTreeNode *> nodes = root->children();
189  const auto constNodes = nodes;
190  for ( QgsLayerTreeNode *node : constNodes )
191  root->takeChild( node );
192  delete root;
193 
194  rootGroup->insertChildNodes( -1, nodes );
195 
196  return true;
197 
198 }
199 
200 bool QgsLayerDefinition::exportLayerDefinition( QString path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
201 {
202  if ( !path.endsWith( QLatin1String( ".qlr" ) ) )
203  path = path.append( ".qlr" );
204 
205  QFile file( path );
206 
207  if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
208  {
209  errorMessage = file.errorString();
210  return false;
211  }
212 
213  QgsReadWriteContext context;
214  bool writeAbsolutePath = QgsProject::instance()->readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
215  context.setPathResolver( QgsPathResolver( writeAbsolutePath ? QString() : path ) );
216 
217  QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
218  if ( !exportLayerDefinition( doc, selectedTreeNodes, errorMessage, context ) )
219  return false;
220 
221  QTextStream qlayerstream( &file );
222  doc.save( qlayerstream, 2 );
223  return true;
224 }
225 
226 bool QgsLayerDefinition::exportLayerDefinition( QDomDocument doc, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage, const QgsReadWriteContext &context )
227 {
228  Q_UNUSED( errorMessage )
229  QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
230  doc.appendChild( qgiselm );
231  QList<QgsLayerTreeNode *> nodes = selectedTreeNodes;
233  const auto constNodes = nodes;
234  for ( QgsLayerTreeNode *node : constNodes )
235  {
236  QgsLayerTreeNode *newnode = node->clone();
237  root->addChildNode( newnode );
238  }
239  root->writeXml( qgiselm, context );
240 
241  QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
242  QList<QgsLayerTreeLayer *> layers = root->findLayers();
243  const auto constLayers = layers;
244  for ( QgsLayerTreeLayer *layer : constLayers )
245  {
246  if ( ! layer->layer() )
247  {
248  QgsDebugMsgLevel( QStringLiteral( "Not a valid map layer: skipping %1" ).arg( layer->name( ) ), 4 );
249  continue;
250  }
251  QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
252  layer->layer()->writeLayerXml( layerelm, doc, context );
253  layerselm.appendChild( layerelm );
254  }
255  qgiselm.appendChild( layerselm );
256  return true;
257 }
258 
259 QDomDocument QgsLayerDefinition::exportLayerDefinitionLayers( const QList<QgsMapLayer *> &layers, const QgsReadWriteContext &context )
260 {
261  QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
262  QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
263  doc.appendChild( qgiselm );
264  QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
265  const auto constLayers = layers;
266  for ( QgsMapLayer *layer : constLayers )
267  {
268  QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
269  layer->writeLayerXml( layerelm, doc, context );
270  layerselm.appendChild( layerelm );
271  }
272  qgiselm.appendChild( layerselm );
273  return doc;
274 }
275 
276 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( QDomDocument &document, QgsReadWriteContext &context )
277 {
278  QList<QgsMapLayer *> layers;
279  QDomNodeList layernodes = document.elementsByTagName( QStringLiteral( "maplayer" ) );
280  for ( int i = 0; i < layernodes.size(); ++i )
281  {
282  QDomNode layernode = layernodes.at( i );
283  QDomElement layerElem = layernode.toElement();
284 
285  QString type = layerElem.attribute( QStringLiteral( "type" ) );
286  QgsDebugMsg( type );
287  QgsMapLayer *layer = nullptr;
288 
289  if ( type == QLatin1String( "vector" ) )
290  {
291  layer = new QgsVectorLayer( );
292  }
293  else if ( type == QLatin1String( "raster" ) )
294  {
295  layer = new QgsRasterLayer;
296  }
297  else if ( type == QLatin1String( "vector-tile" ) )
298  {
299  layer = new QgsVectorTileLayer;
300  }
301  else if ( type == QLatin1String( "plugin" ) )
302  {
303  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
305  }
306 
307  if ( !layer )
308  continue;
309 
310  // always add the layer, even if the source is invalid -- this allows users to fix the source
311  // at a later stage and still retain all the layer properties intact
312  layer->readLayerXml( layerElem, context );
313  layers << layer;
314  }
315  return layers;
316 }
317 
318 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( const QString &qlrfile )
319 {
320  QFile file( qlrfile );
321  if ( !file.open( QIODevice::ReadOnly ) )
322  {
323  QgsDebugMsg( QStringLiteral( "Can't open file" ) );
324  return QList<QgsMapLayer *>();
325  }
326 
327  QDomDocument doc;
328  if ( !doc.setContent( &file ) )
329  {
330  QgsDebugMsg( QStringLiteral( "Can't set content" ) );
331  return QList<QgsMapLayer *>();
332  }
333 
334  QgsReadWriteContext context;
335  context.setPathResolver( QgsPathResolver( qlrfile ) );
336  //no project translator defined here
337  return QgsLayerDefinition::loadLayerDefinitionLayers( doc, context );
338 }
339 
340 
341 void QgsLayerDefinition::DependencySorter::init( const QDomDocument &doc )
342 {
343  // Determine a loading order of layers based on a graph of dependencies
344  QMap< QString, QVector< QString > > dependencies;
345  QStringList sortedLayers;
346  QList< QPair<QString, QDomNode> > layersToSort;
347  QStringList layerIds;
348 
349  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
350  layerIds.reserve( nl.count() );
351  QVector<QString> deps; //avoid expensive allocation for list for every iteration
352  for ( int i = 0; i < nl.count(); i++ )
353  {
354  deps.resize( 0 ); // preserve capacity - don't use clear
355  QDomNode node = nl.item( i );
356 
357  QString id = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
358  layerIds << id;
359 
360  // dependencies for this layer
361  QDomElement layerDependenciesElem = node.firstChildElement( QStringLiteral( "layerDependencies" ) );
362  if ( !layerDependenciesElem.isNull() )
363  {
364  QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName( QStringLiteral( "layer" ) );
365  for ( int j = 0; j < dependencyList.size(); ++j )
366  {
367  QDomElement depElem = dependencyList.at( j ).toElement();
368  deps << depElem.attribute( QStringLiteral( "id" ) );
369  }
370  }
371  dependencies[id] = deps;
372 
373  if ( deps.empty() )
374  {
375  sortedLayers << id;
376  mSortedLayerNodes << node;
377  mSortedLayerIds << id;
378  }
379  else
380  layersToSort << qMakePair( id, node );
381  }
382 
383  // check that all dependencies are present
384  const auto constDependencies = dependencies;
385  for ( const QVector< QString > &ids : constDependencies )
386  {
387  const auto constIds = ids;
388  for ( const QString &depId : constIds )
389  {
390  if ( !dependencies.contains( depId ) )
391  {
392  // some dependencies are not satisfied
393  mHasMissingDependency = true;
394  for ( int i = 0; i < nl.size(); i++ )
395  mSortedLayerNodes << nl.at( i );
396  mSortedLayerIds = layerIds;
397  return;
398  }
399  }
400  }
401 
402  // cycles should be very rare, since layers with cyclic dependencies may only be created by
403  // manually modifying the project file
404  mHasCycle = false;
405 
406  while ( !layersToSort.empty() && !mHasCycle )
407  {
408  QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
409  while ( it != layersToSort.end() )
410  {
411  QString idToSort = it->first;
412  QDomNode node = it->second;
413  mHasCycle = true;
414  bool resolved = true;
415  const auto deps { dependencies.value( idToSort ) };
416  for ( const QString &dep : deps )
417  {
418  if ( !sortedLayers.contains( dep ) )
419  {
420  resolved = false;
421  break;
422  }
423  }
424  if ( resolved ) // dependencies for this layer are resolved
425  {
426  sortedLayers << idToSort;
427  mSortedLayerNodes << node;
428  mSortedLayerIds << idToSort;
429  it = layersToSort.erase( it ); // erase and go to the next
430  mHasCycle = false;
431  }
432  else
433  {
434  ++it;
435  }
436  }
437  }
438 }
439 
441  : mHasCycle( false )
442  , mHasMissingDependency( false )
443 {
444  init( doc );
445 }
446 
448  : mHasCycle( false )
449  , mHasMissingDependency( false )
450 {
451  QString qgsProjectFile = fileName;
452  QgsProjectArchive archive;
453  if ( fileName.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
454  {
455  archive.unzip( fileName );
456  qgsProjectFile = archive.projectFile();
457  }
458 
459  QDomDocument doc;
460  QFile pFile( qgsProjectFile );
461  ( void )pFile.open( QIODevice::ReadOnly );
462  ( void )doc.setContent( &pFile );
463  init( doc );
464 }
465 
466 
QgsLayerTreeGroup::findLayers
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Definition: qgslayertreegroup.cpp:223
QgsReadWriteContext::setPathResolver
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
Definition: qgsreadwritecontext.cpp:52
QgsLayerTreeNode
Definition: qgslayertreenode.h:74
QgsVectorTileLayer
Definition: qgsvectortilelayer.h:83
qgsrasterlayer.h
QgsMapLayer::generateId
static QString generateId(const QString &layerName)
Generates an unique identifier for this layer, the generate ID is prefixed by layerName.
Definition: qgsmaplayer.cpp:1869
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
QgsProjectArchive::unzip
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:142
QgsLayerTreeGroup::resolveReferences
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
Definition: qgslayertreegroup.cpp:352
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
qgspluginlayerregistry.h
qgsreadwritecontext.h
QgsLayerTreeGroup::writeXml
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element.
Definition: qgslayertreegroup.cpp:299
QgsLayerTreeGroup::readChildrenFromXml
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
Definition: qgslayertreegroup.cpp:320
qgspathresolver.h
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:458
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsProject::readBoolEntry
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Definition: qgsproject.cpp:2519
QgsReadWriteContext::setProjectTranslator
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
Definition: qgsreadwritecontext.cpp:87
QgsProject
Definition: qgsproject.h:92
QgsLayerDefinition::DependencySorter::sortedLayerNodes
QVector< QDomNode > sortedLayerNodes() const
Gets the layer nodes in an order where they can be loaded incrementally without dependency break.
Definition: qgslayerdefinition.h:100
qgsapplication.h
qgsvectortilelayer.h
QgsProject::addMapLayers
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Definition: qgsproject.cpp:3252
QgsLayerDefinition::DependencySorter::DependencySorter
DependencySorter(const QDomDocument &doc)
Constructor.
Definition: qgslayerdefinition.cpp:440
QgsProjectArchive::projectFile
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
Definition: qgsarchive.cpp:129
QgsLayerTreeLayer
Definition: qgslayertreelayer.h:43
qgsmaplayer.h
QgsPluginLayerRegistry::createLayer
QgsPluginLayer * createLayer(const QString &typeName, const QString &uri=QString())
Returns new layer if corresponding plugin has been found else returns nullptr.
Definition: qgspluginlayerregistry.cpp:117
QgsLayerTreeGroup
Definition: qgslayertreegroup.h:34
QgsLayerTreeGroup::insertChildNodes
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
Definition: qgslayertreegroup.cpp:99
typeName
const QString & typeName
Definition: qgswfsgetfeature.cpp:109
QgsRasterLayer
Definition: qgsrasterlayer.h:72
qgslayertree.h
QgsLayerDefinition::loadLayerDefinitionLayers
static QList< QgsMapLayer * > loadLayerDefinitionLayers(QDomDocument &document, QgsReadWriteContext &context)
Creates new layers from a layer definition document.
Definition: qgslayerdefinition.cpp:276
qgsvectorlayer.h
qgslayerdefinition.h
QgsLayerDefinition::loadLayerDefinition
static bool loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage)
Loads the QLR at path into QGIS. New layers are added to given project into layer tree specified by r...
Definition: qgslayerdefinition.cpp:34
QgsLayerDefinition::exportLayerDefinition
static bool exportLayerDefinition(QString path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Export the selected layer tree nodes to a QLR file.
Definition: qgslayerdefinition.cpp:200
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsLayerTreeGroup::addChildNode
void addChildNode(QgsLayerTreeNode *node)
Append an existing node.
Definition: qgslayertreegroup.cpp:127
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsMapLayer::readLayerXml
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
Definition: qgsmaplayer.cpp:218
QgsLayerTreeNode::children
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Definition: qgslayertreenode.h:112
QgsLayerTreeNode::takeChild
bool takeChild(QgsLayerTreeNode *node)
Remove a child from a node.
Definition: qgslayertreenode.cpp:273
qgspluginlayer.h
QgsLayerTreeNode::clone
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
QgsLayerDefinition::DependencySorter
Definition: qgslayerdefinition.h:83
QgsProjectArchive
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:115
qgslogger.h
QgsLayerDefinition::exportLayerDefinitionLayers
static QDomDocument exportLayerDefinitionLayers(const QList< QgsMapLayer * > &layers, const QgsReadWriteContext &context)
Returns the given layer as a layer definition document Layer definitions store the data source as wel...
Definition: qgslayerdefinition.cpp:259
QgsLayerDefinition::DependencySorter::hasMissingDependency
bool hasMissingDependency() const
Whether some dependency is missing.
Definition: qgslayerdefinition.h:109
QgsApplication::pluginLayerRegistry
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
Definition: qgsapplication.cpp:2169
QgsPathResolver
Definition: qgspathresolver.h:31
qgsproject.h