diff --git a/python/core/auto_generated/processing/qgsprocessingcontext.sip.in b/python/core/auto_generated/processing/qgsprocessingcontext.sip.in index f03ff9988df..34e0be8fa1b 100644 --- a/python/core/auto_generated/processing/qgsprocessingcontext.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingcontext.sip.in @@ -160,6 +160,13 @@ Ownership of ``processor`` is transferred. .. seealso:: :py:func:`postProcessor` .. versionadded:: 3.2 +%End + + void setOutputLayerName( QgsMapLayer *layer ) const; +%Docstring +Sets a ``layer`` name to match this output, respecting any local user settings which affect this name. + +.. versionadded:: 3.10.1 %End QgsProject *project; diff --git a/python/plugins/processing/core/ProcessingConfig.py b/python/plugins/processing/core/ProcessingConfig.py index e766a855a49..1d289497b58 100644 --- a/python/plugins/processing/core/ProcessingConfig.py +++ b/python/plugins/processing/core/ProcessingConfig.py @@ -49,7 +49,7 @@ class ProcessingConfig: VECTOR_LINE_STYLE = 'VECTOR_LINE_STYLE' VECTOR_POLYGON_STYLE = 'VECTOR_POLYGON_STYLE' FILTER_INVALID_GEOMETRIES = 'FILTER_INVALID_GEOMETRIES' - USE_FILENAME_AS_LAYER_NAME = 'USE_FILENAME_AS_LAYER_NAME' + PREFER_FILENAME_AS_LAYER_NAME = 'PREFER_FILENAME_AS_LAYER_NAME' KEEP_DIALOG_OPEN = 'KEEP_DIALOG_OPEN' PRE_EXECUTION_SCRIPT = 'PRE_EXECUTION_SCRIPT' POST_EXECUTION_SCRIPT = 'POST_EXECUTION_SCRIPT' @@ -74,8 +74,8 @@ class ProcessingConfig: ProcessingConfig.tr('Keep dialog open after running an algorithm'), True)) ProcessingConfig.addSetting(Setting( ProcessingConfig.tr('General'), - ProcessingConfig.USE_FILENAME_AS_LAYER_NAME, - ProcessingConfig.tr('Use filename as layer name'), False)) + ProcessingConfig.PREFER_FILENAME_AS_LAYER_NAME, + ProcessingConfig.tr('Prefer output filename for layer names'), True)) ProcessingConfig.addSetting(Setting( ProcessingConfig.tr('General'), ProcessingConfig.SHOW_PROVIDERS_TOOLTIP, diff --git a/python/plugins/processing/gui/Postprocessing.py b/python/plugins/processing/gui/Postprocessing.py index 9029f5606b3..395f9d5bbf5 100644 --- a/python/plugins/processing/gui/Postprocessing.py +++ b/python/plugins/processing/gui/Postprocessing.py @@ -17,7 +17,6 @@ *************************************************************************** """ - __author__ = 'Victor Olaya' __date__ = 'August 2012' __copyright__ = '(C) 2012, Victor Olaya' @@ -41,30 +40,6 @@ from processing.core.ProcessingConfig import ProcessingConfig from processing.gui.RenderingStyles import RenderingStyles -def set_layer_name(layer, context_layer_details): - """ - Sets the name for the given layer, either using the layer's file name - (or database layer name), or the name specified by the parameter definition. - """ - use_filename_as_layer_name = ProcessingConfig.getSetting(ProcessingConfig.USE_FILENAME_AS_LAYER_NAME) - - if use_filename_as_layer_name or not context_layer_details.name: - source_parts = QgsProviderRegistry.instance().decodeUri(layer.dataProvider().name(), layer.source()) - layer_name = source_parts.get('layerName', '') - # if source layer name exists, use that -- else use - if layer_name: - layer.setName(layer_name) - else: - path = source_parts.get('path', '') - if path: - layer.setName(os.path.splitext(os.path.basename(path))[0]) - elif context_layer_details.name: - # fallback to parameter's name -- shouldn't happen! - layer.setName(context_layer_details.name) - else: - layer.setName(context_layer_details.name) - - def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parameters={}): wrongLayers = [] if feedback is None: @@ -82,7 +57,7 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parame try: layer = QgsProcessingUtils.mapLayerFromString(l, context, typeHint=details.layerTypeHint) if layer is not None: - set_layer_name(layer, details) + details.setOutputLayerName(layer) '''If running a model, the execution will arrive here when an algorithm that is part of that model is executed. We check if its output is a final otuput of the model, and @@ -126,7 +101,9 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parame else: wrongLayers.append(str(l)) except Exception: - QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) + QgsMessageLog.logMessage(QCoreApplication.translate('Postprocessing', + "Error loading result layer:") + "\n" + traceback.format_exc(), + 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 @@ -135,7 +112,8 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parame if wrongLayers: msg = QCoreApplication.translate('Postprocessing', "The following layers were not correctly generated.") msg += "" - msg += QCoreApplication.translate('Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.") + msg += QCoreApplication.translate('Postprocessing', + "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm.") feedback.reportError(msg) return len(wrongLayers) == 0 diff --git a/src/core/processing/qgsprocessingcontext.cpp b/src/core/processing/qgsprocessingcontext.cpp index 5065727336f..a6adb6b1459 100644 --- a/src/core/processing/qgsprocessingcontext.cpp +++ b/src/core/processing/qgsprocessingcontext.cpp @@ -17,6 +17,8 @@ #include "qgsprocessingcontext.h" #include "qgsprocessingutils.h" +#include "qgsproviderregistry.h" +#include "qgssettings.h" QgsProcessingContext::QgsProcessingContext() : mPreferredVectorFormat( QgsProcessingUtils::defaultVectorExtension() ) @@ -119,3 +121,39 @@ void QgsProcessingContext::LayerDetails::setPostProcessor( QgsProcessingLayerPos mPostProcessor = processor; } + +void QgsProcessingContext::LayerDetails::setOutputLayerName( QgsMapLayer *layer ) const +{ + if ( !layer ) + return; + + const bool preferFilenameAsLayerName = QgsSettings().value( QStringLiteral( "Processing/Configuration/PREFER_FILENAME_AS_LAYER_NAME" ), true ).toBool(); + + // note - for temporary layers, we don't use the filename, regardless of user setting (it will be meaningless!) + if ( ( preferFilenameAsLayerName && !layer->isTemporary() ) || name.isEmpty() ) + { + const QVariantMap sourceParts = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() ); + const QString layerName = sourceParts.value( QStringLiteral( "layerName" ) ).toString(); + // if output layer name exists, use that! + if ( !layerName.isEmpty() ) + layer->setName( layerName ); + else + { + const QString path = sourceParts.value( QStringLiteral( "path" ) ).toString(); + if ( !path.isEmpty() ) + { + const QFileInfo fi( path ); + layer->setName( fi.baseName() ); + } + else if ( !name.isEmpty() ) + { + // fallback to parameter's name -- shouldn't happen! + layer->setName( name ); + } + } + } + else + { + layer->setName( name ); + } +} diff --git a/src/core/processing/qgsprocessingcontext.h b/src/core/processing/qgsprocessingcontext.h index fa3eb50840f..d59112ec53f 100644 --- a/src/core/processing/qgsprocessingcontext.h +++ b/src/core/processing/qgsprocessingcontext.h @@ -172,10 +172,17 @@ class CORE_EXPORT QgsProcessingContext //! Default constructor LayerDetails() = default; - //! Friendly name for layer, to use when loading layer into project. + /** + * Friendly name for layer, possibly for use when loading layer into project. + * + * \warning Instead of directly using this value, prefer to call setOutputLayerName() to + * generate a layer name which respects the user's local Processing settings. + */ QString name; - //! Associated output name from algorithm which generated the layer. + /** + * Associated output name from algorithm which generated the layer. + */ QString outputName; /** @@ -202,6 +209,13 @@ class CORE_EXPORT QgsProcessingContext */ void setPostProcessor( QgsProcessingLayerPostProcessorInterface *processor SIP_TRANSFER ); + /** + * Sets a \a layer name to match this output, respecting any local user settings which affect this name. + * + * \since QGIS 3.10.1 + */ + void setOutputLayerName( QgsMapLayer *layer ) const; + //! Destination project QgsProject *project = nullptr; diff --git a/tests/src/analysis/testqgsprocessing.cpp b/tests/src/analysis/testqgsprocessing.cpp index 4ec30e95d56..075b9fafc86 100644 --- a/tests/src/analysis/testqgsprocessing.cpp +++ b/tests/src/analysis/testqgsprocessing.cpp @@ -1938,6 +1938,27 @@ void TestQgsProcessing::parameters() QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), destId ); QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "my_dest" ) ); QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).outputName, QStringLiteral( "fs" ) ); + + // setting layer name to match... + context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( nullptr ); + std::unique_ptr< QgsVectorLayer > vl = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "Point" ), QString(), QStringLiteral( "memory" ) ); + QVERIFY( vl->isValid() ); + context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( vl.get() ); + // temporary layer, must use output name as layer name + QCOMPARE( vl->name(), QStringLiteral( "my_dest" ) ); + // otherwise expect to use path + std::unique_ptr< QgsRasterLayer > rl = qgis::make_unique< QgsRasterLayer >( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif", QString() ); + context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( rl.get() ); + QCOMPARE( rl->name(), QStringLiteral( "landsat" ) ); + // unless setting prohibits it... + QgsSettings().setValue( QStringLiteral( "Processing/Configuration/PREFER_FILENAME_AS_LAYER_NAME" ), false ); + context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( rl.get() ); + QCOMPARE( rl->name(), QStringLiteral( "my_dest" ) ); + // if layer has a layername, we should use that instead of the base file name... + QgsSettings().setValue( QStringLiteral( "Processing/Configuration/PREFER_FILENAME_AS_LAYER_NAME" ), true ); + vl = qgis::make_unique< QgsVectorLayer >( QStringLiteral( TEST_DATA_DIR ) + "/points_gpkg.gpkg|layername=points_small", QString() ); + context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( vl.get() ); + QCOMPARE( vl->name(), QStringLiteral( "points_small" ) ); } void TestQgsProcessing::algorithmParameters()