Skip to content

Commit

Permalink
[processing] Refine behavior of "Use filename as layer name" option to
Browse files Browse the repository at this point in the history
always avoid using temporary file names, and then set this option as
enabled by default.

Also change the setting key so that existing users will also get the new
default value.

Fixes #32591
  • Loading branch information
nyalldawson committed Nov 9, 2019
1 parent 0bf60da commit 208cb81
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 33 deletions.
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions python/plugins/processing/core/ProcessingConfig.py
Expand Up @@ -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'
Expand All @@ -74,8 +74,8 @@ def initialize():
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,
Expand Down
34 changes: 6 additions & 28 deletions python/plugins/processing/gui/Postprocessing.py
Expand Up @@ -17,7 +17,6 @@
***************************************************************************
"""


__author__ = 'Victor Olaya'
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'
Expand All @@ -41,30 +40,6 @@
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:
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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 += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>"
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
38 changes: 38 additions & 0 deletions src/core/processing/qgsprocessingcontext.cpp
Expand Up @@ -17,6 +17,8 @@

#include "qgsprocessingcontext.h"
#include "qgsprocessingutils.h"
#include "qgsproviderregistry.h"
#include "qgssettings.h"

QgsProcessingContext::QgsProcessingContext()
: mPreferredVectorFormat( QgsProcessingUtils::defaultVectorExtension() )
Expand Down Expand Up @@ -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 );
}
}
18 changes: 16 additions & 2 deletions src/core/processing/qgsprocessingcontext.h
Expand Up @@ -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;

/**
Expand All @@ -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;

Expand Down
21 changes: 21 additions & 0 deletions tests/src/analysis/testqgsprocessing.cpp
Expand Up @@ -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()
Expand Down

0 comments on commit 208cb81

Please sign in to comment.