mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -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
|
%End
|
||||||
|
|
||||||
|
|
||||||
static void setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
static QString setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
||||||
%Docstring
|
%Docstring
|
||||||
Sets a path pre-processor function, which allows for manipulation of paths and data sources prior
|
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.
|
to resolving them to file references or layer sources.
|
||||||
@ -85,9 +85,10 @@ Example - replace stored database credentials with new ones:
|
|||||||
.. versionadded:: 3.10
|
.. versionadded:: 3.10
|
||||||
%End
|
%End
|
||||||
%MethodCode
|
%MethodCode
|
||||||
|
PyObject *s = 0;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
Py_XINCREF( a0 );
|
Py_XINCREF( a0 );
|
||||||
QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
QString id = QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
||||||
{
|
{
|
||||||
QString res;
|
QString res;
|
||||||
SIP_BLOCK_THREADS
|
SIP_BLOCK_THREADS
|
||||||
@ -104,7 +105,29 @@ Example - replace stored database credentials with new ones:
|
|||||||
return res;
|
return res;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
s = sipConvertFromNewType( new QString( id ), sipType_QString, 0 );
|
||||||
Py_END_ALLOW_THREADS
|
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
|
%End
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -19,8 +19,9 @@
|
|||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QUrl>
|
#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 )
|
QgsPathResolver::QgsPathResolver( const QString &baseFileName )
|
||||||
: mBaseFileName( baseFileName )
|
: mBaseFileName( baseFileName )
|
||||||
@ -30,7 +31,11 @@ QgsPathResolver::QgsPathResolver( const QString &baseFileName )
|
|||||||
|
|
||||||
QString QgsPathResolver::readPath( const QString &f ) const
|
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() )
|
if ( filename.isEmpty() )
|
||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
@ -144,9 +149,21 @@ QString QgsPathResolver::readPath( const QString &f ) const
|
|||||||
return vsiPrefix + projElems.join( QStringLiteral( "/" ) );
|
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
|
QString QgsPathResolver::writePath( const QString &src ) const
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup core
|
* \ingroup core
|
||||||
@ -54,11 +55,16 @@ class CORE_EXPORT QgsPathResolver
|
|||||||
*
|
*
|
||||||
* The path pre-processor function is called before any bad layer handler.
|
* 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
|
* \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
|
#else
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,11 +106,12 @@ class CORE_EXPORT QgsPathResolver
|
|||||||
*
|
*
|
||||||
* \since QGIS 3.10
|
* \since QGIS 3.10
|
||||||
*/
|
*/
|
||||||
static void setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
static QString setPathPreprocessor( SIP_PYCALLABLE / AllowNone / );
|
||||||
% MethodCode
|
% MethodCode
|
||||||
|
PyObject *s = 0;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
Py_XINCREF( a0 );
|
Py_XINCREF( a0 );
|
||||||
QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
QString id = QgsPathResolver::setPathPreprocessor( [a0]( const QString &arg )->QString
|
||||||
{
|
{
|
||||||
QString res;
|
QString res;
|
||||||
SIP_BLOCK_THREADS
|
SIP_BLOCK_THREADS
|
||||||
@ -121,7 +128,43 @@ class CORE_EXPORT QgsPathResolver
|
|||||||
return res;
|
return res;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
s = sipConvertFromNewType( new QString( id ), sipType_QString, 0 );
|
||||||
Py_END_ALLOW_THREADS
|
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
|
% End
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -129,7 +172,7 @@ class CORE_EXPORT QgsPathResolver
|
|||||||
//! path to a file that is the base for relative path resolution
|
//! path to a file that is the base for relative path resolution
|
||||||
QString mBaseFileName;
|
QString mBaseFileName;
|
||||||
|
|
||||||
static std::function< QString( const QString & ) > sCustomResolver;
|
static std::vector< std::pair< QString, std::function< QString( const QString & ) > > > sCustomResolvers;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSPATHRESOLVER_H
|
#endif // QGSPATHRESOLVER_H
|
||||||
|
@ -28,20 +28,75 @@ class TestQgsPathResolver(unittest.TestCase):
|
|||||||
|
|
||||||
def testCustomPreprocessor(self):
|
def testCustomPreprocessor(self):
|
||||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'aaaaa')
|
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'aaaaa')
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
QgsPathResolver().removePathPreprocessor('bad')
|
||||||
|
|
||||||
def run_test():
|
def run_test():
|
||||||
def my_processor(path):
|
def my_processor(path):
|
||||||
return path.upper()
|
return path.upper()
|
||||||
|
|
||||||
QgsPathResolver.setPathPreprocessor(my_processor)
|
id = QgsPathResolver.setPathPreprocessor(my_processor)
|
||||||
|
self.assertTrue(id)
|
||||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'AAAAA')
|
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'AAAAA')
|
||||||
|
return id
|
||||||
|
|
||||||
run_test()
|
id = run_test()
|
||||||
gc.collect()
|
gc.collect()
|
||||||
# my_processor should be out of scope and cleaned up, unless things are working
|
# my_processor should be out of scope and cleaned up, unless things are working
|
||||||
# correctly and ownership was transferred
|
# correctly and ownership was transferred
|
||||||
self.assertEqual(QgsPathResolver().readPath('aaaaa'), 'AAAAA')
|
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):
|
def testLoadLayerWithPreprocessor(self):
|
||||||
"""
|
"""
|
||||||
Test that custom path preprocessor is used when loading layers
|
Test that custom path preprocessor is used when loading layers
|
||||||
|
Loading…
x
Reference in New Issue
Block a user