Skip to content
Permalink
Browse files

[processing][FEATURE] New parameter type for map scales

This adds a new parameter type specifically for map scales,
QgsProcessingParameterScale. The values are evaluated using
self.parameterAsDouble, which returns the map scale
denominator (matching the standard in other parts of the
QGIS API).

Scale parameters are displayed to users using the standard
QgsScaleWidget, which includes the combo box of predefined
scales and a shortcut button to match the current map scale.
  • Loading branch information
nyalldawson committed Mar 13, 2019
1 parent ca7008c commit 32fbf0a92b772a6ac7d9f4c41ff815d7dac80e90
@@ -151,6 +151,8 @@ their acceptable ranges, defaults, etc.
sipType = sipType_QgsProcessingParameterNumber;
else if ( sipCpp->type() == QgsProcessingParameterDistance::typeName() )
sipType = sipType_QgsProcessingParameterDistance;
else if ( sipCpp->type() == QgsProcessingParameterScale::typeName() )
sipType = sipType_QgsProcessingParameterScale;
else if ( sipCpp->type() == QgsProcessingParameterRange::typeName() )
sipType = sipType_QgsProcessingParameterRange;
else if ( sipCpp->type() == QgsProcessingParameterRasterLayer::typeName() )
@@ -1653,6 +1655,49 @@ Sets the default distance ``unit`` for the parameter.
virtual bool fromVariantMap( const QVariantMap &map );


};

class QgsProcessingParameterScale : QgsProcessingParameterNumber
{
%Docstring
A double numeric parameter for map scale values.

QgsProcessingParameterScale should be evaluated by calling QgsProcessingAlgorithm.parameterAsDouble(),
which will return a numeric value representing the scale denominator.

.. versionadded:: 3.8
%End

%TypeHeaderCode
#include "qgsprocessingparameters.h"
%End
public:

explicit QgsProcessingParameterScale( const QString &name, const QString &description = QString(),
const QVariant &defaultValue = QVariant(),
bool optional = false );
%Docstring
Constructor for QgsProcessingParameterScale.
%End

static QString typeName();
%Docstring
Returns the type name for the parameter class.
%End

virtual QgsProcessingParameterScale *clone() const /Factory/;


virtual QString type() const;

virtual QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const;


static QgsProcessingParameterScale *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
%Docstring
Creates a new parameter using the definition from a script code.
%End

};

class QgsProcessingParameterRange : QgsProcessingParameterDefinition
@@ -66,6 +66,7 @@

PARAMETER_NUMBER = 'number'
PARAMETER_DISTANCE = 'distance'
PARAMETER_SCALE = 'scale'
PARAMETER_RASTER = 'raster'
PARAMETER_TABLE = 'vector'
PARAMETER_VECTOR = 'source'
@@ -55,6 +55,7 @@
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterScale,
QgsProcessingParameterRange,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterEnum,
@@ -224,8 +225,8 @@ def setupUi(self):
if self.param is not None:
self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.layerType()))
self.verticalLayout.addWidget(self.datatypeCombo)
elif (self.paramType == parameters.PARAMETER_NUMBER or self.paramType == parameters.PARAMETER_DISTANCE or
isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance))):
elif (self.paramType in (parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE) or
isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale))):

if (self.paramType == parameters.PARAMETER_DISTANCE or
isinstance(self.param, QgsProcessingParameterDistance)):
@@ -245,7 +246,8 @@ def setupUi(self):
self.parentCombo.setCurrentIndex(idx)
idx += 1
self.verticalLayout.addWidget(self.parentCombo)
else:
elif (self.paramType != parameters.PARAMETER_SCALE and not
isinstance(self.param, QgsProcessingParameterScale)):
self.verticalLayout.addWidget(QLabel(self.tr('Number type')))
self.type_combo = QComboBox()
self.type_combo.addItem(self.tr('Float'), QgsProcessingParameterNumber.Double)
@@ -254,15 +256,17 @@ def setupUi(self):
self.type_combo.setCurrentIndex(self.type_combo.findData(self.param.dataType()))
self.verticalLayout.addWidget(self.type_combo)

self.verticalLayout.addWidget(QLabel(self.tr('Min value')))
self.minTextBox = QLineEdit()
self.verticalLayout.addWidget(self.minTextBox)
self.verticalLayout.addWidget(QLabel(self.tr('Max value')))
self.maxTextBox = QLineEdit()
self.verticalLayout.addWidget(self.maxTextBox)
if self.param is not None:
self.minTextBox.setText(str(self.param.minimum()))
self.maxTextBox.setText(str(self.param.maximum()))
if (self.paramType != parameters.PARAMETER_SCALE and not
isinstance(self.param, QgsProcessingParameterScale)):
self.verticalLayout.addWidget(QLabel(self.tr('Min value')))
self.minTextBox = QLineEdit()
self.verticalLayout.addWidget(self.minTextBox)
self.verticalLayout.addWidget(QLabel(self.tr('Max value')))
self.maxTextBox = QLineEdit()
self.verticalLayout.addWidget(self.maxTextBox)
if self.param is not None:
self.minTextBox.setText(str(self.param.minimum()))
self.maxTextBox.setText(str(self.param.maximum()))
self.verticalLayout.addWidget(QLabel(self.tr('Default value')))
self.defaultTextBox = QLineEdit()
self.defaultTextBox.setText(self.tr('0'))
@@ -475,6 +479,10 @@ def accept(self):
parent = self.parentCombo.currentData()
if parent:
self.param.setParentParameterName(parent)
elif (self.paramType == parameters.PARAMETER_SCALE or
isinstance(self.param, QgsProcessingParameterScale)):
self.param = QgsProcessingParameterScale(name, description,
self.defaultTextBox.text())
elif (self.paramType == parameters.PARAMETER_NUMBER or
isinstance(self.param, QgsProcessingParameterNumber)):

@@ -641,6 +641,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
// "static"/single value sources
QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
<< QgsProcessingParameterDistance::typeName()
<< QgsProcessingParameterScale::typeName()
<< QgsProcessingParameterBoolean::typeName()
<< QgsProcessingParameterExpression::typeName()
<< QgsProcessingParameterField::typeName()
@@ -1626,6 +1626,10 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromScriptCo
return QgsProcessingParameterMultipleLayers::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "number" ) )
return QgsProcessingParameterNumber::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "distance" ) )
return QgsProcessingParameterDistance::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "scale" ) )
return QgsProcessingParameterScale::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "range" ) )
return QgsProcessingParameterRange::fromScriptCode( name, description, isOptional, definition );
else if ( type == QStringLiteral( "raster" ) )
@@ -5259,6 +5263,50 @@ bool QgsProcessingParameterDistance::fromVariantMap( const QVariantMap &map )
}


//
// QgsProcessingParameterScale
//

QgsProcessingParameterScale::QgsProcessingParameterScale( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterNumber( name, description, Double, defaultValue, optional )
{

}

QgsProcessingParameterScale *QgsProcessingParameterScale::clone() const
{
return new QgsProcessingParameterScale( *this );
}

QString QgsProcessingParameterScale::type() const
{
return typeName();
}

QString QgsProcessingParameterScale::asPythonString( const QgsProcessing::PythonOutputType outputType ) const
{
switch ( outputType )
{
case QgsProcessing::PythonQgsProcessingAlgorithmSubclass:
{
QString code = QStringLiteral( "QgsProcessingParameterScale('%1', '%2'" ).arg( name(), description() );
if ( mFlags & FlagOptional )
code += QStringLiteral( ", optional=True" );
QgsProcessingContext c;
code += QStringLiteral( ", defaultValue=%1)" ).arg( valueAsPythonString( mDefault, c ) );
return code;
}
}
return QString();
}

QgsProcessingParameterScale *QgsProcessingParameterScale::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
return new QgsProcessingParameterScale( name, description, definition.isEmpty() ? QVariant()
: ( definition.toLower().trimmed() == QStringLiteral( "none" ) ? QVariant() : definition ), isOptional );
}


//
// QgsProcessingParameterLayout
//
@@ -228,6 +228,8 @@ class CORE_EXPORT QgsProcessingParameterDefinition
sipType = sipType_QgsProcessingParameterNumber;
else if ( sipCpp->type() == QgsProcessingParameterDistance::typeName() )
sipType = sipType_QgsProcessingParameterDistance;
else if ( sipCpp->type() == QgsProcessingParameterScale::typeName() )
sipType = sipType_QgsProcessingParameterScale;
else if ( sipCpp->type() == QgsProcessingParameterRange::typeName() )
sipType = sipType_QgsProcessingParameterRange;
else if ( sipCpp->type() == QgsProcessingParameterRasterLayer::typeName() )
@@ -1628,6 +1630,44 @@ class CORE_EXPORT QgsProcessingParameterDistance : public QgsProcessingParameter

};

/**
* \class QgsProcessingParameterDistance
* \ingroup core
* A double numeric parameter for map scale values.
*
* QgsProcessingParameterScale should be evaluated by calling QgsProcessingAlgorithm::parameterAsDouble(),
* which will return a numeric value representing the scale denominator.
*
* \since QGIS 3.8
*/
class CORE_EXPORT QgsProcessingParameterScale : public QgsProcessingParameterNumber
{
public:

/**
* Constructor for QgsProcessingParameterScale.
*/
explicit QgsProcessingParameterScale( const QString &name, const QString &description = QString(),
const QVariant &defaultValue = QVariant(),
bool optional = false );

/**
* Returns the type name for the parameter class.
*/
static QString typeName() { return QStringLiteral( "scale" ); }

QgsProcessingParameterScale *clone() const override SIP_FACTORY;

QString type() const override;
QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const override;

/**
* Creates a new parameter using the definition from a script code.
*/
static QgsProcessingParameterScale *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) SIP_FACTORY;

};

/**
* \class QgsProcessingParameterRange
* \ingroup core
@@ -1340,6 +1340,61 @@ class CORE_EXPORT QgsProcessingParameterTypeDistance : public QgsProcessingParam
}


};


/**
* A scale parameter for processing algorithms.
*
* \ingroup core
* \note No Python bindings available. Get your copy from QgsApplication.processingRegistry().parameterType('scale')
* \since QGIS 3.8
*/
class CORE_EXPORT QgsProcessingParameterTypeScale : public QgsProcessingParameterType
{
QgsProcessingParameterDefinition *create( const QString &name ) const override SIP_FACTORY
{
return new QgsProcessingParameterScale( name );
}

QString description() const override
{
return QCoreApplication::translate( "Processing", "A numeric parameter representing a map scale." );
}

QString name() const override
{
return QCoreApplication::translate( "Processing", "Scale" );
}

QString id() const override
{
return QStringLiteral( "scale" );
}

QString pythonImportString() const override
{
return QStringLiteral( "from qgis.core import QgsProcessingParameterScale" );
}

QString className() const override
{
return QStringLiteral( "QgsProcessingParameterScale" );
}

QStringList acceptedPythonTypes() const override
{
return QStringList() << QStringLiteral( "int: scale denominator" )
<< QStringLiteral( "float: scale denominator" )
<< QStringLiteral( "QgsProperty" );
}

QStringList acceptedStringValues() const override
{
return QStringList() << QObject::tr( "A numeric value representing the scale denominator" );
}


};

/**
@@ -46,6 +46,7 @@ QgsProcessingRegistry::QgsProcessingRegistry( QObject *parent SIP_TRANSFERTHIS )
addParameterType( new QgsProcessingParameterTypeFeatureSource() );
addParameterType( new QgsProcessingParameterTypeNumber() );
addParameterType( new QgsProcessingParameterTypeDistance() );
addParameterType( new QgsProcessingParameterTypeScale() );
addParameterType( new QgsProcessingParameterTypeBand() );
addParameterType( new QgsProcessingParameterTypeFeatureSink() );
addParameterType( new QgsProcessingParameterTypeLayout() );
@@ -32,6 +32,7 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry()
addParameterWidgetFactory( new QgsProcessingStringWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingNumericWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingDistanceWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingScaleWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingRangeWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingAuthConfigWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingMatrixWidgetWrapper() );

2 comments on commit 32fbf0a

@havatv

This comment has been minimized.

Copy link
Contributor

@havatv havatv replied Mar 14, 2020

Great addition!
It would be nice to have an ALG decorator for it. Could it be added to python/processing/algfactory.py (and python/plugins/processing/core/parameters.py)?

@nyalldawson

This comment has been minimized.

Copy link
Collaborator Author

@nyalldawson nyalldawson replied Mar 15, 2020

@havatv it's already there

Please sign in to comment.
You can’t perform that action at this time.