Skip to content

Commit

Permalink
[processing][FEATURE] New parameter type for map scales
Browse files Browse the repository at this point in the history
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 32fbf0a
Show file tree
Hide file tree
Showing 13 changed files with 514 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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() )
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions python/plugins/processing/core/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

PARAMETER_NUMBER = 'number'
PARAMETER_DISTANCE = 'distance'
PARAMETER_SCALE = 'scale'
PARAMETER_RASTER = 'raster'
PARAMETER_TABLE = 'vector'
PARAMETER_VECTOR = 'source'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterScale,
QgsProcessingParameterRange,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterEnum,
Expand Down Expand Up @@ -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)):
Expand All @@ -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)
Expand All @@ -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'))
Expand Down Expand Up @@ -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)):

Expand Down
1 change: 1 addition & 0 deletions src/core/processing/models/qgsprocessingmodelalgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
48 changes: 48 additions & 0 deletions src/core/processing/qgsprocessingparameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" ) )
Expand Down Expand Up @@ -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
//
Expand Down
40 changes: 40 additions & 0 deletions src/core/processing/qgsprocessingparameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() )
Expand Down Expand Up @@ -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
Expand Down
55 changes: 55 additions & 0 deletions src/core/processing/qgsprocessingparametertypeimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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" );
}


};

/**
Expand Down
1 change: 1 addition & 0 deletions src/core/processing/qgsprocessingregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() );
Expand Down
1 change: 1 addition & 0 deletions src/gui/processing/qgsprocessingguiregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() );
Expand Down
Loading

2 comments on commit 32fbf0a

@havatv
Copy link
Contributor

@havatv havatv commented on 32fbf0a Mar 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@havatv it's already there

Please sign in to comment.