diff --git a/python/core/auto_generated/qgsapplication.sip.in b/python/core/auto_generated/qgsapplication.sip.in index c83163800373..d17149f4d985 100644 --- a/python/core/auto_generated/qgsapplication.sip.in +++ b/python/core/auto_generated/qgsapplication.sip.in @@ -857,6 +857,17 @@ Gets the registry of available scalebar renderers. Returns registry of available project storage implementations. .. versionadded:: 3.2 +%End + + static QgsLocalizedDataPathRegistry *localizedDataPathRegistry() /KeepReference/; +%Docstring +Returns the registry of data repositories +These are used as paths for basemaps, logos, etc. which can be referenced +differently across work stations. + +.. seealso:: :py:class:`QgsLocalizedDataPathRegistry` + +.. versionadded:: 3.14 %End static QString nullRepresentation(); diff --git a/python/core/auto_generated/qgslocalizeddatapathregistry.sip.in b/python/core/auto_generated/qgslocalizeddatapathregistry.sip.in new file mode 100644 index 000000000000..4fdc3d1ff1a5 --- /dev/null +++ b/python/core/auto_generated/qgslocalizeddatapathregistry.sip.in @@ -0,0 +1,71 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgslocalizeddatapathregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsLocalizedDataPathRegistry +{ +%Docstring +A registry class to hold localized data paths which can be used for basemaps, logos, etc. +Paths are meant to be absolute paths and are stored by order of preference. + +If a layer from one of the paths is loaded, it will be saved as localized in the project file. +For instance, if you have C:\my_maps in your localized paths, +C:\my_maps\my_country\ortho.tif will be save in your project as localized:my_country\ortho.tif + +The resolving of the file paths happens in QgsPathResolver. + +.. versionadded:: 3.14 +%End + +%TypeHeaderCode +#include "qgslocalizeddatapathregistry.h" +%End + public: + QgsLocalizedDataPathRegistry(); + + QString globalPath( const QString &localizedPath ) const; +%Docstring +Returns the global path if the file has been found in one of the paths, an empty string otherwise +%End + + QString localizedPath( const QString &globalPath ) const; +%Docstring +Returns the localized path if the file has been found in one of the path, an empty string otherwise +%End + + QStringList paths() const; +%Docstring +Returns a list of registered localized paths +%End + + + void registerPath( const QString &path, int position = -1 ); +%Docstring +Registers a localized path +If ``position`` is given, the path is inserted at the given position in the list +Since the paths are stored by order of preference, lower positions in the list take precedence. +%End + + void unregisterPath( const QString &path ); +%Docstring +Unregisters a localized path +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgslocalizeddatapathregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index ec7b4127d099..10e0af629226 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -99,6 +99,7 @@ %Include auto_generated/qgslegendsettings.sip %Include auto_generated/qgslegendstyle.sip %Include auto_generated/qgslocaldefaultsettings.sip +%Include auto_generated/qgslocalizeddatapathregistry.sip %Include auto_generated/qgslogger.sip %Include auto_generated/qgsmapdecoration.sip %Include auto_generated/qgsmaphittest.sip diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 4d49fee1937f..8f1200ede234 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -40,6 +40,7 @@ #include "qgsnumericformatwidget.h" #include "qgsattributetablefiltermodel.h" +#include "qgslocalizeddatapathregistry.h" #include "qgsrasterformatsaveoptionswidget.h" #include "qgsrasterpyramidsoptionswidget.h" #include "qgsdatumtransformtablewidget.h" @@ -308,6 +309,21 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QListpaths(); + for ( const QString &path : localizedPaths ) + { + QListWidgetItem *newItem = new QListWidgetItem( mLocalizedDataPathListWidget ); + newItem->setText( path ); + newItem->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable ); + mLocalizedDataPathListWidget->addItem( newItem ); + } + //paths hidden from browser const QStringList hiddenPathList = mSettings->value( QStringLiteral( "/browser/hiddenPaths" ) ).toStringList(); for ( const QString &path : hiddenPathList ) @@ -1409,6 +1425,11 @@ void QgsOptions::saveOptions() } mSettings->setValue( QStringLiteral( "Layout/searchPathsForTemplates" ), pathsList, QgsSettings::Core ); + pathsList.clear(); + for ( int r = 0; r < mLocalizedDataPathListWidget->count(); r++ ) + pathsList << mLocalizedDataPathListWidget->item( r )->text(); + QgsApplication::localizedDataPathRegistry()->setPaths( pathsList ); + pathsList.clear(); for ( int i = 0; i < mListHiddenBrowserPaths->count(); ++i ) { @@ -2528,6 +2549,55 @@ void QgsOptions::addColor() mTreeCustomColors->addColor( newColor, QgsSymbolLayerUtils::colorToName( newColor ) ); } +void QgsOptions::removeLocalizedDataPath() +{ + qDeleteAll( mLocalizedDataPathListWidget->selectedItems() ); +} + +void QgsOptions::addLocalizedDataPath() +{ + QString myDir = QFileDialog::getExistingDirectory( + this, + tr( "Choose a Directory" ), + QDir::homePath(), + QFileDialog::ShowDirsOnly + ); + + if ( ! myDir.isEmpty() ) + { + QListWidgetItem *newItem = new QListWidgetItem( mLocalizedDataPathListWidget ); + newItem->setText( myDir ); + newItem->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable ); + mLocalizedDataPathListWidget->addItem( newItem ); + mLocalizedDataPathListWidget->setCurrentItem( newItem ); + } +} + +void QgsOptions::moveLocalizedDataPathUp() +{ + QList selectedItems = mLocalizedDataPathListWidget->selectedItems(); + QList::iterator itemIt = selectedItems.begin(); + for ( ; itemIt != selectedItems.end(); ++itemIt ) + { + int row = mLocalizedDataPathListWidget->row( *itemIt ); + mLocalizedDataPathListWidget->takeItem( row ); + mLocalizedDataPathListWidget->insertItem( row - 1, *itemIt ); + } +} + +void QgsOptions::moveLocalizedDataPathDown() +{ + QList selectedItems = mLocalizedDataPathListWidget->selectedItems(); + QList::iterator itemIt = selectedItems.begin(); + for ( ; itemIt != selectedItems.end(); ++itemIt ) + { + int row = mLocalizedDataPathListWidget->row( *itemIt ); + mLocalizedDataPathListWidget->takeItem( row ); + mLocalizedDataPathListWidget->insertItem( row + 1, *itemIt ); + } +} + + QListWidgetItem *QgsOptions::addScaleToScaleList( const QString &newScale ) { QListWidgetItem *newItem = new QListWidgetItem( newScale ); diff --git a/src/app/qgsoptions.h b/src/app/qgsoptions.h index ae69e4866b93..8a17f5d6bbda 100644 --- a/src/app/qgsoptions.h +++ b/src/app/qgsoptions.h @@ -241,6 +241,12 @@ class APP_EXPORT QgsOptions : public QgsOptionsDialogBase, private Ui::QgsOption void addColor(); + private slots: + void removeLocalizedDataPath(); + void addLocalizedDataPath(); + void moveLocalizedDataPathUp(); + void moveLocalizedDataPathDown(); + private: QgsSettings *mSettings = nullptr; QStringList i18nList(); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9fd6ced3a3c5..de9d8df43e35 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -306,6 +306,7 @@ SET(QGIS_CORE_SRCS qgslegendstyle.cpp qgslocaldefaultsettings.cpp qgslocalec.cpp + qgslocalizeddatapathregistry.cpp qgslogger.cpp qgsmapdecoration.cpp qgsmaphittest.cpp @@ -854,6 +855,7 @@ SET(QGIS_CORE_HDRS qgslegendstyle.h qgslocaldefaultsettings.h qgslocalec.h + qgslocalizeddatapathregistry.h qgslogger.h qgsmapdecoration.h qgsmaphittest.h diff --git a/src/core/qgsapplication.cpp b/src/core/qgsapplication.cpp index 44aebbac7ff3..0dc1f3bf0ed0 100644 --- a/src/core/qgsapplication.cpp +++ b/src/core/qgsapplication.cpp @@ -15,6 +15,7 @@ #include "qgsapplication.h" #include "qgsauthmanager.h" +#include "qgslocalizeddatapathregistry.h" #include "qgsdataitemproviderregistry.h" #include "qgsexception.h" #include "qgsgeometry.h" @@ -2235,10 +2236,16 @@ QgsProjectStorageRegistry *QgsApplication::projectStorageRegistry() return members()->mProjectStorageRegistry; } +QgsLocalizedDataPathRegistry *QgsApplication::localizedDataPathRegistry() +{ + return members()->mLocalizedDataPathRegistry; +} + QgsApplication::ApplicationMembers::ApplicationMembers() { // don't use initializer lists or scoped pointers - as more objects are added here we // will need to be careful with the order of creation/destruction + mLocalizedDataPathRegistry = new QgsLocalizedDataPathRegistry(); mMessageLog = new QgsMessageLog(); mProfiler = new QgsRuntimeProfiler(); @@ -2402,6 +2409,7 @@ QgsApplication::ApplicationMembers::~ApplicationMembers() delete mNumericFormatRegistry; delete mBookmarkManager; delete mConnectionRegistry; + delete mLocalizedDataPathRegistry; } QgsApplication::ApplicationMembers *QgsApplication::members() diff --git a/src/core/qgsapplication.h b/src/core/qgsapplication.h index ca1ce0214342..9d89115a0a99 100644 --- a/src/core/qgsapplication.h +++ b/src/core/qgsapplication.h @@ -32,6 +32,7 @@ class QgsFieldFormatterRegistry; class QgsColorSchemeRegistry; class QgsPaintEffectRegistry; class QgsProjectStorageRegistry; +class QgsLocalizedDataPathRegistry; class QgsRendererRegistry; class QgsSvgCache; class QgsImageCache; @@ -789,6 +790,15 @@ class CORE_EXPORT QgsApplication : public QApplication */ static QgsProjectStorageRegistry *projectStorageRegistry() SIP_KEEPREFERENCE; + /** + * Returns the registry of data repositories + * These are used as paths for basemaps, logos, etc. which can be referenced + * differently across work stations. + * \see QgsLocalizedDataPathRegistry + * \since QGIS 3.14 + */ + static QgsLocalizedDataPathRegistry *localizedDataPathRegistry() SIP_KEEPREFERENCE; + /** * This string is used to represent the value `NULL` throughout QGIS. * @@ -913,6 +923,7 @@ class CORE_EXPORT QgsApplication : public QApplication QgsActionScopeRegistry *mActionScopeRegistry = nullptr; QgsAnnotationRegistry *mAnnotationRegistry = nullptr; QgsColorSchemeRegistry *mColorSchemeRegistry = nullptr; + QgsLocalizedDataPathRegistry *mLocalizedDataPathRegistry = nullptr; QgsNumericFormatRegistry *mNumericFormatRegistry = nullptr; QgsFieldFormatterRegistry *mFieldFormatterRegistry = nullptr; QgsGpsConnectionRegistry *mGpsConnectionRegistry = nullptr; diff --git a/src/core/qgslocalizeddatapathregistry.cpp b/src/core/qgslocalizeddatapathregistry.cpp new file mode 100644 index 000000000000..cb184aad1f48 --- /dev/null +++ b/src/core/qgslocalizeddatapathregistry.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + qgslocalizeddatapathregistry.cpp + -------------------------------------- + Date : May 2020 + Copyright : (C) 2020 by Denis Rouzaud + Email : denis.rouzaud + *************************************************************************** + * * + * 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 + +#include "qgslocalizeddatapathregistry.h" +#include "qgssettings.h" +#include "qgis.h" +#include "qgsreadwritelocker.h" + + +QgsLocalizedDataPathRegistry::QgsLocalizedDataPathRegistry() +{ + readFromSettings(); +} + +QString QgsLocalizedDataPathRegistry::globalPath( const QString &relativePath ) const +{ + for ( const QDir &basePath : qgis::as_const( mPaths ) ) + if ( basePath.exists( relativePath ) ) + return basePath.absoluteFilePath( relativePath ); + + return QString(); +} + +QString QgsLocalizedDataPathRegistry::localizedPath( const QString &fullPath ) const +{ + for ( const QDir &basePath : qgis::as_const( mPaths ) ) + if ( fullPath.startsWith( basePath.absolutePath() ) ) + return basePath.relativeFilePath( fullPath ); + + return QString(); + +} + +QStringList QgsLocalizedDataPathRegistry::paths() const +{ + QStringList paths; + for ( const QDir &dir : mPaths ) + paths << dir.absolutePath(); + return paths; +} + +void QgsLocalizedDataPathRegistry::setPaths( const QStringList &paths ) +{ + mPaths.clear(); + for ( const QString &path : paths ) + { + QDir dir( path ); + if ( !mPaths.contains( dir ) ) + mPaths << dir; + } + + writeToSettings(); +} + +void QgsLocalizedDataPathRegistry::registerPath( const QString &path, int position ) +{ + QDir dir( path ); + if ( mPaths.contains( dir ) ) + return; + + if ( position >= 0 && position < mPaths.count() ) + mPaths.insert( position, dir ); + else + mPaths.append( dir ); + + writeToSettings(); +} + +void QgsLocalizedDataPathRegistry::unregisterPath( const QString &path ) +{ + mPaths.removeAll( QDir( path ) ); + writeToSettings(); +} + +void QgsLocalizedDataPathRegistry::readFromSettings() +{ + setPaths( QgsSettings().value( QStringLiteral( "/qgis/localized_data_paths" ) ).toStringList() ); +} + +void QgsLocalizedDataPathRegistry::writeToSettings() +{ + QgsSettings().setValue( QStringLiteral( "/qgis/localized_data_paths" ), paths() ); +} diff --git a/src/core/qgslocalizeddatapathregistry.h b/src/core/qgslocalizeddatapathregistry.h new file mode 100644 index 000000000000..6dc118330cca --- /dev/null +++ b/src/core/qgslocalizeddatapathregistry.h @@ -0,0 +1,74 @@ +/*************************************************************************** + qgslocalizeddatapathregistry.h + -------------------------------------- + Date : May 2020 + Copyright : (C) 2020 by Denis Rouzaud + Email : denis.rouzaud + *************************************************************************** + * * + * 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 QGSLOCALIZEDDATAPATHREGISTRY_H +#define QGSLOCALIZEDDATAPATHREGISTRY_H + + +#include +#include + +#include "qgis_core.h" +#include "qgis_sip.h" + +/** + * \ingroup core + * A registry class to hold localized data paths which can be used for basemaps, logos, etc. + * Paths are meant to be absolute paths and are stored by order of preference. + * + * If a layer from one of the paths is loaded, it will be saved as localized in the project file. + * For instance, if you have C:\my_maps in your localized paths, + * C:\my_maps\my_country\ortho.tif will be save in your project as localized:my_country\ortho.tif + * + * The resolving of the file paths happens in QgsPathResolver. + * + * \since QGIS 3.14 + */ +class CORE_EXPORT QgsLocalizedDataPathRegistry +{ + public: + QgsLocalizedDataPathRegistry(); + + //! Returns the global path if the file has been found in one of the paths, an empty string otherwise + QString globalPath( const QString &localizedPath ) const; + + //! Returns the localized path if the file has been found in one of the path, an empty string otherwise + QString localizedPath( const QString &globalPath ) const; + + //! Returns a list of registered localized paths + QStringList paths() const; + + //! Sets the complete list of localized path + void setPaths( const QStringList &paths ) SIP_SKIP; + + /** + * Registers a localized path + * If \a position is given, the path is inserted at the given position in the list + * Since the paths are stored by order of preference, lower positions in the list take precedence. + */ + void registerPath( const QString &path, int position = -1 ); + + //! Unregisters a localized path + void unregisterPath( const QString &path ); + + private: + void readFromSettings(); + void writeToSettings(); + + QList mPaths; +}; + +#endif // QGSLOCALIZEDDATAPATHREGISTRY_H diff --git a/src/core/qgspathresolver.cpp b/src/core/qgspathresolver.cpp index bde687c6da21..90914712d578 100644 --- a/src/core/qgspathresolver.cpp +++ b/src/core/qgspathresolver.cpp @@ -14,6 +14,7 @@ ***************************************************************************/ #include "qgspathresolver.h" +#include "qgslocalizeddatapathregistry.h" #include "qgis.h" #include "qgsapplication.h" @@ -21,6 +22,7 @@ #include #include + typedef std::vector< std::pair< QString, std::function< QString( const QString & ) > > > CustomResolvers; Q_GLOBAL_STATIC( CustomResolvers, sCustomResolvers ) @@ -48,6 +50,12 @@ QString QgsPathResolver::readPath( const QString &f ) const return QgsApplication::pkgDataPath() + QStringLiteral( "/resources" ) + src.mid( 8 ); } + if ( src.startsWith( QLatin1String( "localized:" ) ) ) + { + // strip away "localized:" prefix, replace with actual inbuilt data folder path + return QgsApplication::localizedDataPathRegistry()->globalPath( src.mid( 10 ) ) ; + } + if ( mBaseFileName.isNull() ) { return src; @@ -183,6 +191,10 @@ QString QgsPathResolver::writePath( const QString &src ) const return src; } + QString localizedPath = QgsApplication::localizedDataPathRegistry()->localizedPath( src ); + if ( !localizedPath.isEmpty() ) + return QStringLiteral( "localized:" ) + localizedPath; + if ( src.startsWith( QgsApplication::pkgDataPath() + QStringLiteral( "/resources" ) ) ) { // replace inbuilt data folder path with "inbuilt:" prefix diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 480d600bdbb6..b01528439fa0 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -332,7 +332,7 @@ - 13 + 3 @@ -361,8 +361,8 @@ 0 0 - 843 - 917 + 645 + 1003 @@ -1118,8 +1118,8 @@ 0 0 - 541 - 1072 + 539 + 1114 @@ -1645,8 +1645,8 @@ 0 0 - 552 - 509 + 555 + 501 @@ -1858,8 +1858,8 @@ 0 0 - 499 - 615 + 848 + 816 @@ -2059,41 +2059,106 @@ - + - Hidden browser paths + Localized data paths - + - + - Paths hidden from browser panel + Localized data paths for basemaps, logos, etc. (in order of preference) - + Qt::Horizontal - 31 + 40 20 - - - - - 0 - 120 - + + + + Add localized data path + + + + + + + :/images/themes/default/symbologyAdd.svg:/images/themes/default/symbologyAdd.svg + + + + Remove localized data path + + + + + + + :/images/themes/default/symbologyRemove.svg:/images/themes/default/symbologyRemove.svg + + + + + + + Raise selected localized data path priority + + + + + + + :/images/themes/default/mActionArrowUp.svg:/images/themes/default/mActionArrowUp.svg + + + + + + + Lower selected localized data path priority + + + + + + + :/images/themes/default/mActionArrowDown.svg:/images/themes/default/mActionArrowDown.svg + + + + + + + QAbstractItemView::MultiSelection + + + QAbstractItemView::SelectRows + + + + + + + + + + Hidden browser paths + + @@ -2108,6 +2173,36 @@ + + + + Paths hidden from browser panel + + + + + + + Qt::Horizontal + + + + 31 + 20 + + + + + + + + + 0 + 120 + + + + @@ -2160,8 +2255,8 @@ 0 0 - 674 - 1057 + 695 + 1139 @@ -2946,8 +3041,8 @@ 0 0 - 501 - 293 + 529 + 309 @@ -3257,8 +3352,8 @@ 0 0 - 604 - 671 + 606 + 741 @@ -3762,8 +3857,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 157 - 265 + 181 + 318 @@ -3930,8 +4025,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 543 - 801 + 570 + 827 @@ -4555,8 +4650,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 480 - 538 + 469 + 572 @@ -4824,8 +4919,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 412 - 372 + 506 + 367 @@ -4993,8 +5088,8 @@ The bigger the number, the faster zooming with the mouse wheel will be. 0 0 - 857 - 682 + 633 + 720 @@ -5481,7 +5576,7 @@ The bigger the number, the faster zooming with the mouse wheel will be. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Noto Sans'; font-size:10pt;"><br /></p></body></html> @@ -5598,17 +5693,17 @@ p, li { white-space: pre-wrap; }
qgscollapsiblegroupbox.h
1 + + QgsFilterLineEdit + QLineEdit +
qgsfilterlineedit.h
+
QgsColorButton QToolButton
qgscolorbutton.h
1
- - QgsFilterLineEdit - QLineEdit -
qgsfilterlineedit.h
-
QgsProjectionSelectionWidget QWidget @@ -5884,7 +5979,6 @@ p, li { white-space: pre-wrap; } - diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 687d74a737a9..9af4b3d30ecf 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -143,6 +143,7 @@ ADD_PYTHON_TEST(PyQgsLegendRenderer test_qgslegendrenderer.py) ADD_PYTHON_TEST(PyQgsLineSegment test_qgslinesegment.py) ADD_PYTHON_TEST(PyQgsLineSymbolLayers test_qgslinesymbollayers.py) ADD_PYTHON_TEST(PyQgsLocalDefaultSettings test_qgslocaldefaultsettings.py) +ADD_PYTHON_TEST(PyQgsLocalizedDataPathRegistry test_qgslocalizeddatapathregistry.py) ADD_PYTHON_TEST(PyQgsLocator test_qgslocator.py) ADD_PYTHON_TEST(PyQgsMapCanvas test_qgsmapcanvas.py) ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py) diff --git a/tests/src/python/test_qgslocalizeddatapathregistry.py b/tests/src/python/test_qgslocalizeddatapathregistry.py new file mode 100644 index 000000000000..69aefc3145b5 --- /dev/null +++ b/tests/src/python/test_qgslocalizeddatapathregistry.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsLocalizedDataPathRegistry. + +.. note:: 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. +""" +__author__ = 'Denis Rouzaud' +__date__ = '13/05/2020' +__copyright__ = 'Copyright 2019, The QGIS Project' + +import qgis # NOQA + +from tempfile import NamedTemporaryFile, gettempdir +from pathlib import Path +import os +import gc +from qgis.core import ( + QgsLocalizedDataPathRegistry, + QgsApplication, + QgsPathResolver, + QgsProject, + QgsVectorLayer +) +from qgis.testing import start_app, unittest +from utilities import unitTestDataPath + +start_app() + +MAP_PATH = "data/world_map.gpkg" +BASE_PATH = QgsApplication.pkgDataPath() + '/resources' +ABSOLUTE_PATH = '{}/{}'.format(BASE_PATH, MAP_PATH) + + +class TestQgsLocalizedDataPathRegistry(unittest.TestCase): + """ + Test resolving and saving localized data paths + """ + + def setUp(self): + QgsApplication.localizedDataPathRegistry().registerPath(BASE_PATH) + + def tearDown(self): + QgsApplication.localizedDataPathRegistry().unregisterPath(BASE_PATH) + + def testQgsLocalizedDataPathRegistry(self): + self.assertEqual(QgsApplication.localizedDataPathRegistry().localizedPath(ABSOLUTE_PATH), MAP_PATH) + self.assertEqual(QgsApplication.localizedDataPathRegistry().globalPath(MAP_PATH), ABSOLUTE_PATH) + + def testOrderOfPreference(self): + temp_dir = gettempdir() + os.mkdir('{}/data'.format(temp_dir)) + alt_dir = '{}/{}'.format(temp_dir, MAP_PATH) + Path(alt_dir).touch() + QgsApplication.localizedDataPathRegistry().registerPath(temp_dir, 0) + self.assertEqual(QgsApplication.localizedDataPathRegistry().globalPath(MAP_PATH), alt_dir) + QgsApplication.localizedDataPathRegistry().unregisterPath(temp_dir) + + def testWithResolver(self): + self.assertEqual(QgsPathResolver().readPath('localized:' + MAP_PATH), ABSOLUTE_PATH) + self.assertEqual(QgsPathResolver().writePath(ABSOLUTE_PATH), 'localized:' + MAP_PATH) + + def testProject(self): + layer = QgsVectorLayer('{}|layername=countries'.format(ABSOLUTE_PATH), 'Test', 'ogr') + + # write + p = QgsProject() + fh = NamedTemporaryFile(delete=False) + p.setFileName(fh.name) + p.addMapLayer(layer) + self.assertTrue(p.write()) + found = False + with open(fh.name) as fh: + for line in fh: + if 'localized:data/world_map.gpkg|layername=countries' in line: + found = True + break + self.assertTrue(found) + + # read + p2 = QgsProject() + p2.setFileName(fh.name) + p2.read() + self.assertTrue(len(p2.mapLayers())) + self.assertEqual(p2.mapLayers()[layer.id()].source(), '{}/{}|layername=countries'.format(BASE_PATH, MAP_PATH)) + + os.remove(fh.name) + + +if __name__ == '__main__': + unittest.main()