Skip to content
Permalink
Browse files
Add API to export layers to QLR with easy control over whether
absolute or relative paths are used
  • Loading branch information
nyalldawson committed Jul 2, 2021
1 parent ee141a1 commit 39fbd77df7568c859d5c62ca793dcac177e2f97c
@@ -33,13 +33,40 @@ Loads the QLR at path into QGIS. New layers are added to given project into lay
%Docstring
Loads the QLR from the XML document. New layers are added to given project into layer tree specified by rootGroup
%End
static bool exportLayerDefinition( QString path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage /Out/ );

static bool exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage /Out/ );
%Docstring
Exports the selected layer tree nodes to a QLR file.

This method uses the :py:func:`QgsProject.instance()`'s file path setting to determine whether absolute
or relative paths are written. Use the variant with an explicit argument for file path type
for control over this setting.

:param path: file path for exported QLR file
:param selectedTreeNodes: layer tree nodes to include in the QLR file

:return: - ``True`` if the export was successful
- errorMessage: will be set to any error messages generated during the export
%End

static bool exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, Qgis::FilePathType pathType, QString &errorMessage /Out/ );
%Docstring
Export the selected layer tree nodes to a QLR file
Exports the selected layer tree nodes to a QLR file.

:param path: file path for exported QLR file
:param selectedTreeNodes: layer tree nodes to include in the QLR file
:param pathType: specifies whether absolute or relative file paths should be used.

:return: - ``True`` if the export was successful
- errorMessage: will be set to any error messages generated during the export


.. versionadded:: 3.22
%End

static bool exportLayerDefinition( QDomDocument doc, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage /Out/, const QgsReadWriteContext &context );
%Docstring
Export the selected layer tree nodes to a QLR-XML document
Exports the selected layer tree nodes to a QLR XML document.
%End

static QDomDocument exportLayerDefinitionLayers( const QList<QgsMapLayer *> &layers, const QgsReadWriteContext &context );
@@ -34,6 +34,7 @@
#include "qgsmeshlayer.h"
#include "qgspointcloudlayer.h"
#include "qgsannotationlayer.h"
#include "qgsfileutils.h"

bool QgsLayerDefinition::loadLayerDefinition( const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage )
{
@@ -201,22 +202,32 @@ bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsProject *proj
return true;
}

bool QgsLayerDefinition::exportLayerDefinition( QString path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
bool QgsLayerDefinition::exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
{
if ( !path.endsWith( QLatin1String( ".qlr" ) ) )
path = path.append( ".qlr" );
return exportLayerDefinition( path, selectedTreeNodes, QgsProject::instance()->filePathStorage(), errorMessage );
}

QFile file( path );
bool QgsLayerDefinition::exportLayerDefinition( const QString &p, const QList<QgsLayerTreeNode *> &selectedTreeNodes, Qgis::FilePathType pathType, QString &errorMessage )
{
const QString path = QgsFileUtils::ensureFileNameHasExtension( p, { QStringLiteral( "qlr" )} );

QFile file( path );
if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
{
errorMessage = file.errorString();
return false;
}

QgsReadWriteContext context;
bool writeAbsolutePath = QgsProject::instance()->readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
context.setPathResolver( QgsPathResolver( writeAbsolutePath ? QString() : path ) );
switch ( pathType )
{
case Qgis::FilePathType::Absolute:
context.setPathResolver( QgsPathResolver( QString() ) );
break;
case Qgis::FilePathType::Relative:
context.setPathResolver( QgsPathResolver( path ) );
break;
}

QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
if ( !exportLayerDefinition( doc, selectedTreeNodes, errorMessage, context ) )
@@ -18,6 +18,7 @@

#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgis.h"

#include <QString>
#include <QVector>
@@ -46,9 +47,39 @@ class CORE_EXPORT QgsLayerDefinition
static bool loadLayerDefinition( const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage SIP_OUT );
//! Loads the QLR from the XML document. New layers are added to given project into layer tree specified by rootGroup
static bool loadLayerDefinition( QDomDocument doc, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage SIP_OUT, QgsReadWriteContext &context );
//! Export the selected layer tree nodes to a QLR file
static bool exportLayerDefinition( QString path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage SIP_OUT );
//! Export the selected layer tree nodes to a QLR-XML document

/**
* Exports the selected layer tree nodes to a QLR file.
*
* This method uses the QgsProject::instance()'s file path setting to determine whether absolute
* or relative paths are written. Use the variant with an explicit argument for file path type
* for control over this setting.
*
* \param path file path for exported QLR file
* \param selectedTreeNodes layer tree nodes to include in the QLR file
* \param errorMessage will be set to any error messages generated during the export
*
* \returns TRUE if the export was successful
*/
static bool exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage SIP_OUT );

/**
* Exports the selected layer tree nodes to a QLR file.
*
* \param path file path for exported QLR file
* \param selectedTreeNodes layer tree nodes to include in the QLR file
* \param pathType specifies whether absolute or relative file paths should be used.
* \param errorMessage will be set to any error messages generated during the export
*
* \returns TRUE if the export was successful
*
* \since QGIS 3.22
*/
static bool exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, Qgis::FilePathType pathType, QString &errorMessage SIP_OUT );

/**
* Exports the selected layer tree nodes to a QLR XML document.
*/
static bool exportLayerDefinition( QDomDocument doc, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage SIP_OUT, const QgsReadWriteContext &context );

/**
@@ -11,10 +11,14 @@
__copyright__ = 'Copyright 2016, The QGIS Project'

import os
import shutil
import qgis # NOQA

from qgis.PyQt.QtCore import QTemporaryDir
from qgis.core import (QgsProject,
QgsLayerDefinition
QgsLayerDefinition,
QgsVectorLayer,
Qgis
)

from qgis.testing import unittest, start_app
@@ -130,6 +134,35 @@ def testInvalidSource(self):
self.assertFalse(list(layers.values())[0].isValid())
QgsProject.instance().removeAllMapLayers()

def test_path_storage(self):
"""
Test storage of relative/absolute paths
"""
temp_dir = QTemporaryDir()
gpkg_path = temp_dir.filePath('points_gpkg.gpkg')
shutil.copy(TEST_DATA_DIR + '/points_gpkg.gpkg', gpkg_path)

p = QgsProject()
vl = QgsVectorLayer(gpkg_path)
self.assertTrue(vl.isValid())
p.addMapLayer(vl)

# write qlr with relative paths
ok, err = QgsLayerDefinition.exportLayerDefinition(temp_dir.filePath('relative.qlr'), [p.layerTreeRoot()], Qgis.FilePathType.Relative)
self.assertTrue(ok)

with open(temp_dir.filePath('relative.qlr'), 'rt') as f:
lines = f.readlines()
self.assertIn('source="./points_gpkg.gpkg"', '\n'.join(lines))

# write qlr with absolute paths
ok, err = QgsLayerDefinition.exportLayerDefinition(temp_dir.filePath('absolute.qlr'), [p.layerTreeRoot()], Qgis.FilePathType.Absolute)
self.assertTrue(ok)

with open(temp_dir.filePath('absolute.qlr'), 'rt') as f:
lines = f.readlines()
self.assertIn(f'source="{gpkg_path}"', '\n'.join(lines))


if __name__ == '__main__':
unittest.main()

0 comments on commit 39fbd77

Please sign in to comment.