mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[needs-docs] Revise QgsPathResolver::setPathPreprocessor API
Allow path preprocessors to be chained and don't force replace any existing ones. Processors can be removed via a call to QgsPathResolver::removePathPreprocessor, using the unique ID returned by the original call to setPathPreprocessor
This commit is contained in:
parent
3209cf2282
commit
24fb1196c5
@ -39,7 +39,7 @@ Turn filename read from the project file to an absolute path
|
||||
%End
|
||||
|
||||
|
||||
static void setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
||||
static QString setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
||||
%Docstring
|
||||
Sets a path pre-processor function, which allows for manipulation of paths and data sources prior
|
||||
to resolving them to file references or layer sources.
|
||||
@ -85,9 +85,10 @@ Example - replace stored database credentials with new ones:
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
%MethodCode
|
||||
PyObject *s = 0;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
Py_XINCREF( a0 );
|
||||
QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
||||
QString id = QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
||||
{
|
||||
QString res;
|
||||
SIP_BLOCK_THREADS
|
||||
@ -104,7 +105,29 @@ Example - replace stored database credentials with new ones:
|
||||
return res;
|
||||
} );
|
||||
|
||||
s = sipConvertFromNewType( new QString( id ), sipType_QString, 0 );
|
||||
Py_END_ALLOW_THREADS
|
||||
return s;
|
||||
%End
|
||||
|
||||
|
||||
static void removePathPreprocessor( const QString &id );
|
||||
%Docstring
|
||||
Removes the custom pre-processor function with matching ``id``.
|
||||
|
||||
The ``id`` must correspond to a pre-processor previously added via a call to setPathPreprocessor().
|
||||
An KeyError will be raised if no processor with the specified ``id`` exists.
|
||||
|
||||
.. seealso:: :py:func:`setPathPreprocessor`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
%MethodCode
|
||||
if ( !QgsPathResolver::removePathPreprocessor( *a0 ) )
|
||||
{
|
||||
PyErr_SetString( PyExc_KeyError, QStringLiteral( "No processor with id %1 exists." ).arg( *a0 ).toUtf8().constData() );
|
||||
sipIsErr = 1;
|
||||
}
|
||||
%End
|
||||
|
||||
};
|
||||
|
@ -19,8 +19,9 @@
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
|
||||
std::function< QString( const QString & ) > QgsPathResolver::sCustomResolver = []( const QString &a )->QString { return a; };
|
||||
std::vector< std::pair< QString, std::function< QString( const QString & ) > > > QgsPathResolver::sCustomResolvers;
|
||||
|
||||
QgsPathResolver::QgsPathResolver( const QString &baseFileName )
|
||||
: mBaseFileName( baseFileName )
|
||||
@ -30,7 +31,11 @@ QgsPathResolver::QgsPathResolver( const QString &baseFileName )
|
||||
|
||||
QString QgsPathResolver::readPath( const QString &f ) const
|
||||
{
|
||||
QString filename = sCustomResolver( f );
|
||||
QString filename = f;
|
||||
|
||||
for ( const auto &resolver : sCustomResolvers )
|
||||
filename = resolver.second( filename );
|
||||
|
||||
if ( filename.isEmpty() )
|
||||
return QString();
|
||||
|
||||
@ -144,9 +149,21 @@ QString QgsPathResolver::readPath( const QString &f ) const
|
||||
return vsiPrefix + projElems.join( QStringLiteral( "/" ) );
|
||||
}
|
||||
|
||||
void QgsPathResolver::setPathPreprocessor( const std::function<QString( const QString & )> &processor )
|
||||
QString QgsPathResolver::setPathPreprocessor( const std::function<QString( const QString & )> &processor )
|
||||
{
|
||||
sCustomResolver = processor;
|
||||
QString id = QUuid::createUuid().toString();
|
||||
sCustomResolvers.emplace_back( std::make_pair( id, processor ) );
|
||||
return id;
|
||||
}
|
||||
|
||||
bool QgsPathResolver::removePathPreprocessor( const QString &id )
|
||||
{
|
||||
const size_t prevCount = sCustomResolvers.size();
|
||||
sCustomResolvers.erase( std::remove_if( sCustomResolvers.begin(), sCustomResolvers.end(), [id]( std::pair< QString, std::function< QString( const QString & ) > > &a )
|
||||
{
|
||||
return a.first == id;
|
||||
} ), sCustomResolvers.end() );
|
||||
return prevCount != sCustomResolvers.size();
|
||||
}
|
||||
|
||||
QString QgsPathResolver::writePath( const QString &src ) const
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -54,11 +55,16 @@ class CORE_EXPORT QgsPathResolver
|
||||
*
|
||||
* The path pre-processor function is called before any bad layer handler.
|
||||
*
|
||||
* \note Setting a new \a processor replaces any existing processor.
|
||||
* If multiple preprocessors are set, they will be called in sequence based on the order in which
|
||||
* they were originally set.
|
||||
*
|
||||
* \returns An auto-generated string uniquely identifying the preprocessor, which can later be
|
||||
* used to remove the processor (via a call to removePathPreprocessor()).
|
||||
*
|
||||
* \see removePathPreprocessor()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
static void setPathPreprocessor( const std::function< QString( const QString &filename )> &processor );
|
||||
static QString setPathPreprocessor( const std::function< QString( const QString &filename )> &processor );
|
||||
#else
|
||||
|
||||
/**
|
||||
@ -100,11 +106,12 @@ class CORE_EXPORT QgsPathResolver
|
||||
*
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
static void setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
||||
static QString setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
||||
% MethodCode
|
||||
PyObject *s = 0;
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
Py_XINCREF( a0 );
|
||||
QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
||||
QString id = QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
||||
{
|
||||
QString res;
|
||||
SIP_BLOCK_THREADS
|
||||
@ -121,7 +128,43 @@ class CORE_EXPORT QgsPathResolver
|
||||
return res;
|
||||
} );
|
||||
|
||||
s = sipConvertFromNewType( new QString( id ), sipType_QString, 0 );
|
||||
Py_END_ALLOW_THREADS
|
||||
return s;
|
||||
% End
|
||||
#endif
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
/**
|
||||
* Removes the custom pre-processor function with matching \a id.
|
||||
*
|
||||
* The \a id must correspond to a pre-processor previously added via a call to setPathPreprocessor().
|
||||
*
|
||||
* Returns TRUE if processor existed and was removed.
|
||||
*
|
||||
* \see setPathPreprocessor()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
static bool removePathPreprocessor( const QString &id );
|
||||
#else
|
||||
|
||||
/**
|
||||
* Removes the custom pre-processor function with matching \a id.
|
||||
*
|
||||
* The \a id must correspond to a pre-processor previously added via a call to setPathPreprocessor().
|
||||
* An KeyError will be raised if no processor with the specified \a id exists.
|
||||
*
|
||||
* \see setPathPreprocessor()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
static void removePathPreprocessor( const QString &id );
|
||||
% MethodCode
|
||||
if ( !QgsPathResolver::removePathPreprocessor( *a0 ) )
|
||||
{
|
||||
PyErr_SetString( PyExc_KeyError, QStringLiteral( "No processor with id %1 exists." ).arg( *a0 ).toUtf8().constData() );
|
||||
sipIsErr = 1;
|
||||
}
|
||||
% End
|
||||
#endif
|
||||
|
||||
@ -129,7 +172,7 @@ class CORE_EXPORT QgsPathResolver
|
||||
//! path to a file that is the base for relative path resolution
|
||||
QString mBaseFileName;
|
||||
|
||||
static std::function< QString( const QString & ) > sCustomResolver;
|
||||
static std::vector< std::pair< QString, std::function< QString( const QString & ) > > > sCustomResolvers;
|
||||
};
|
||||
|
||||
#endif // QGSPATHRESOLVER_H
|
||||
|
@ -28,20 +28,75 @@ class TestQgsPathResolver(unittest.TestCase):
|
||||
|
||||
def testCustomPreprocessor(self):
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'aaaaa')
|
||||
with self.assertRaises(KeyError):
|
||||
QgsPathResolver().removePathPreprocessor('bad')
|
||||
|
||||
def run_test():
|
||||
def my_processor(path):
|
||||
return path.upper()
|
||||
|
||||
QgsPathResolver.setPathPreprocessor(my_processor)
|
||||
id = QgsPathResolver.setPathPreprocessor(my_processor)
|
||||
self.assertTrue(id)
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'AAAAA')
|
||||
return id
|
||||
|
||||
run_test()
|
||||
id = run_test()
|
||||
gc.collect()
|
||||
# my_processor should be out of scope and cleaned up, unless things are working
|
||||
# correctly and ownership was transferred
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'AAAAA')
|
||||
|
||||
QgsPathResolver().removePathPreprocessor(id)
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'aaaaa')
|
||||
|
||||
# expect key error
|
||||
with self.assertRaises(KeyError):
|
||||
QgsPathResolver().removePathPreprocessor(id)
|
||||
|
||||
def testChainedPreprocessors(self):
|
||||
"""
|
||||
Test that chaining preprocessors works correctly
|
||||
"""
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'aaaaa')
|
||||
|
||||
def run_test():
|
||||
def my_processor(path):
|
||||
return 'x' + path + 'x'
|
||||
|
||||
def my_processor2(path):
|
||||
return 'y' + path + 'y'
|
||||
|
||||
id = QgsPathResolver.setPathPreprocessor(my_processor)
|
||||
self.assertTrue(id)
|
||||
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'xaaaaax')
|
||||
|
||||
id2 = QgsPathResolver.setPathPreprocessor(my_processor2)
|
||||
self.assertTrue(id2)
|
||||
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'yxaaaaaxy')
|
||||
|
||||
return id, id2
|
||||
|
||||
id, id2 = run_test()
|
||||
gc.collect()
|
||||
# my_processor should be out of scope and cleaned up, unless things are working
|
||||
# correctly and ownership was transferred
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'yxaaaaaxy')
|
||||
|
||||
QgsPathResolver().removePathPreprocessor(id)
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'yaaaaay')
|
||||
|
||||
# expect key error
|
||||
with self.assertRaises(KeyError):
|
||||
QgsPathResolver().removePathPreprocessor(id)
|
||||
|
||||
QgsPathResolver().removePathPreprocessor(id2)
|
||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'aaaaa')
|
||||
|
||||
with self.assertRaises(KeyError):
|
||||
QgsPathResolver().removePathPreprocessor(id2)
|
||||
|
||||
def testLoadLayerWithPreprocessor(self):
|
||||
"""
|
||||
Test that custom path preprocessor is used when loading layers
|
||||
|
Loading…
x
Reference in New Issue
Block a user