Skip to content

Commit

Permalink
[FEATURE][processing] Brand new duration parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jul 26, 2021
1 parent f68ef8f commit 52bac40
Show file tree
Hide file tree
Showing 13 changed files with 544 additions and 4 deletions.
Expand Up @@ -255,6 +255,8 @@ their acceptable ranges, defaults, etc.
sipType = sipType_QgsProcessingParameterNumber;
else if ( sipCpp->type() == QgsProcessingParameterDistance::typeName() )
sipType = sipType_QgsProcessingParameterDistance;
else if ( sipCpp->type() == QgsProcessingParameterDuration::typeName() )
sipType = sipType_QgsProcessingParameterDuration;
else if ( sipCpp->type() == QgsProcessingParameterScale::typeName() )
sipType = sipType_QgsProcessingParameterScale;
else if ( sipCpp->type() == QgsProcessingParameterRange::typeName() )
Expand Down Expand Up @@ -2263,6 +2265,63 @@ Sets the default distance ``unit`` for the parameter.
virtual bool fromVariantMap( const QVariantMap &map );


};

class QgsProcessingParameterDuration : QgsProcessingParameterNumber
{
%Docstring(signature="appended")
A double numeric parameter for duration values. The returned
value will always be in milliseconds.

.. versionadded:: 3.22
%End

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

explicit QgsProcessingParameterDuration( const QString &name, const QString &description = QString(),
const QVariant &defaultValue = QVariant(),
bool optional = false,
double minValue = -DBL_MAX + 1,
double maxValue = DBL_MAX );
%Docstring
Constructor for QgsProcessingParameterDuration.
%End

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

virtual QgsProcessingParameterDuration *clone() const /Factory/;


virtual QString type() const;

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


QgsUnitTypes::TemporalUnit defaultUnit() const;
%Docstring
Returns the default duration unit for the parameter.

.. seealso:: :py:func:`setDefaultUnit`
%End

void setDefaultUnit( QgsUnitTypes::TemporalUnit unit );
%Docstring
Sets the default duration ``unit`` for the parameter.

.. seealso:: :py:func:`defaultUnit`
%End

virtual QVariantMap toVariantMap() const;

virtual bool fromVariantMap( const QVariantMap &map );


};

class QgsProcessingParameterScale : QgsProcessingParameterNumber
Expand Down
47 changes: 47 additions & 0 deletions python/plugins/processing/gui/NumberInputPanel.py
Expand Up @@ -324,3 +324,50 @@ def setValue(self, value):
self.spnValue.setValue(float(value))
except:
return


class DurationInputPanel(NumberInputPanel):
"""
Distance input panel for use outside the modeler - this input panel
contains a label showing the distance unit.
"""

def __init__(self, param):
super().__init__(param)

self.units_combo = QComboBox()
for u in (QgsUnitTypes.TemporalMilliseconds,
QgsUnitTypes.TemporalSeconds,
QgsUnitTypes.TemporalMinutes,
QgsUnitTypes.TemporalHours,
QgsUnitTypes.TemporalDays,
QgsUnitTypes.TemporalWeeks,
QgsUnitTypes.TemporalMonths,
QgsUnitTypes.TemporalYears,
QgsUnitTypes.TemporalDecades,
QgsUnitTypes.TemporalCenturies):
self.units_combo.addItem(QgsUnitTypes.toString(u), u)

self.layout().insertWidget(1, self.units_combo)
self.setUnits(param.defaultUnit())
self.units_combo.show()

self.base_units = param.defaultUnit()

def setUnits(self, units):
self.units_combo.setCurrentIndex(self.units_combo.findData(units))
self.base_units = units

def getValue(self):
val = super().getValue()
if isinstance(val, float) and self.units_combo.isVisible():
display_unit = self.units_combo.currentData()
return val * QgsUnitTypes.fromUnitToUnitFactor(display_unit, self.base_units)

return val

def setValue(self, value):
try:
self.spnValue.setValue(float(value))
except:
return
1 change: 1 addition & 0 deletions python/plugins/processing/gui/TestTools.py
Expand Up @@ -39,6 +39,7 @@
QgsProcessingParameterBoolean,
QgsProcessingParameterNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterDuration,
QgsProcessingParameterFile,
QgsProcessingParameterBand,
QgsProcessingParameterString,
Expand Down
50 changes: 49 additions & 1 deletion python/plugins/processing/gui/wrappers.py
Expand Up @@ -58,6 +58,7 @@
QgsProcessingParameterBand,
QgsProcessingParameterMatrix,
QgsProcessingParameterDistance,
QgsProcessingParameterDuration,
QgsProcessingFeatureSourceDefinition,
QgsProcessingOutputRasterLayer,
QgsProcessingOutputVectorLayer,
Expand Down Expand Up @@ -104,7 +105,7 @@
from processing.core.ProcessingConfig import ProcessingConfig
from processing.modeler.MultilineTextPanel import MultilineTextPanel

from processing.gui.NumberInputPanel import NumberInputPanel, ModelerNumberInputPanel, DistanceInputPanel
from processing.gui.NumberInputPanel import NumberInputPanel, ModelerNumberInputPanel, DistanceInputPanel, DurationInputPanel
from processing.gui.RangePanel import RangePanel
from processing.gui.PointSelectionPanel import PointSelectionPanel
from processing.gui.FileSelectionPanel import FileSelectionPanel
Expand Down Expand Up @@ -901,6 +902,50 @@ def parentParameterChanged(self, wrapper):
self.widget.setUnitParameterValue(wrapper.parameterValue())


class DurationWidgetWrapper(WidgetWrapper):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
"""
.. deprecated:: 3.4
Do not use, will be removed in QGIS 4.0
"""

from warnings import warn
warn("DurationWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning)

def createWidget(self):
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
widget = DurationInputPanel(self.parameterDefinition())
widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
return widget
else:
return ModelerNumberInputPanel(self.parameterDefinition(), self.dialog)

def setValue(self, value):
if value is None or value == NULL:
return

self.widget.setValue(value)

def value(self):
return self.widget.getValue()

def postInitialize(self, wrappers):
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH) and self.parameterDefinition().isDynamic():
for wrapper in wrappers:
if wrapper.parameterDefinition().name() == self.parameterDefinition().dynamicLayerParameterName():
self.widget.setDynamicLayer(wrapper.parameterValue())
wrapper.widgetValueHasChanged.connect(self.parentLayerChanged)
break

def dynamicLayerChanged(self, wrapper):
self.widget.setDynamicLayer(wrapper.parameterValue())

def parentParameterChanged(self, wrapper):
self.widget.setUnitParameterValue(wrapper.parameterValue())


class RangeWidgetWrapper(WidgetWrapper):

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -1907,6 +1952,9 @@ def create_wrapper_from_class(param, dialog, row=0, col=0):
elif param.type() == 'distance':
# deprecated, moved to c++
wrapper = DistanceWidgetWrapper
elif param.type() == 'duration':
# deprecated, moved to c++
wrapper = DurationWidgetWrapper
elif param.type() == 'raster':
# deprecated, moved to c++
wrapper = RasterWidgetWrapper
Expand Down
1 change: 1 addition & 0 deletions python/plugins/processing/tests/GuiTest.py
Expand Up @@ -64,6 +64,7 @@
QgsProcessingParameterBoolean,
QgsProcessingParameterCrs,
QgsProcessingParameterDistance,
QgsProcessingParameterDuration,
QgsProcessingParameterEnum,
QgsProcessingParameterExpression,
QgsProcessingParameterExtent,
Expand Down
1 change: 1 addition & 0 deletions python/processing/algfactory.py
Expand Up @@ -32,6 +32,7 @@
QgsProcessingParameterAuthConfig,
QgsProcessingParameterNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterDuration,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterFileDestination,
Expand Down
8 changes: 5 additions & 3 deletions src/analysis/processing/qgsalgorithmstdbscanclustering.cpp
Expand Up @@ -36,7 +36,7 @@ QString QgsStDbscanClusteringAlgorithm::shortDescription() const

QStringList QgsStDbscanClusteringAlgorithm::tags() const
{
return QObject::tr( "clustering,clusters,density,based,points,temporal,time,interval,distance" ).split( ',' );
return QObject::tr( "clustering,clusters,density,based,points,temporal,time,interval,duration,distance" ).split( ',' );
}

QString QgsStDbscanClusteringAlgorithm::group() const
Expand All @@ -61,8 +61,10 @@ void QgsStDbscanClusteringAlgorithm::initAlgorithm( const QVariantMap & )
QgsProcessingParameterNumber::Integer, 5, false, 1 ) );
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "EPS" ),
QObject::tr( "Maximum distance between clustered points" ), 1, QStringLiteral( "INPUT" ), false, 0 ) );
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "EPS2" ),
QObject::tr( "Maximum date/time interval (in days unit) between clustered points" ), QgsProcessingParameterNumber::Double, 1, false, 0 ) );
QgsProcessingParameterDuration *durationParameter = new QgsProcessingParameterDuration( QStringLiteral( "EPS2" ),
QObject::tr( "Maximum time duration between clustered points" ), 0, false, 0 );
durationParameter->setDefaultUnit( QgsUnitTypes::TemporalDays );
addParameter( durationParameter );

auto dbscanStarParam = std::make_unique<QgsProcessingParameterBoolean>( QStringLiteral( "DBSCAN*" ),
QObject::tr( "Treat border points as noise (DBSCAN*)" ), false, true );
Expand Down
1 change: 1 addition & 0 deletions src/core/processing/models/qgsprocessingmodelalgorithm.cpp
Expand Up @@ -838,6 +838,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
// "static"/single value sources
QgsProcessingModelChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName()
<< QgsProcessingParameterDistance::typeName()
<< QgsProcessingParameterDuration::typeName()
<< QgsProcessingParameterScale::typeName()
<< QgsProcessingParameterBoolean::typeName()
<< QgsProcessingParameterEnum::typeName()
Expand Down
56 changes: 56 additions & 0 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -6843,6 +6843,62 @@ bool QgsProcessingParameterDistance::fromVariantMap( const QVariantMap &map )
}


//
// QgsProcessingParameterDuration
//

QgsProcessingParameterDuration::QgsProcessingParameterDuration( const QString &name, const QString &description, const QVariant &defaultValue, bool optional, double minValue, double maxValue )
: QgsProcessingParameterNumber( name, description, Double, defaultValue, optional, minValue, maxValue )
{
}

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

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

QString QgsProcessingParameterDuration::asPythonString( const QgsProcessing::PythonOutputType outputType ) const
{
switch ( outputType )
{
case QgsProcessing::PythonQgsProcessingAlgorithmSubclass:
{
QString code = QStringLiteral( "QgsProcessingParameterDuration('%1', '%2'" ).arg( name(), description() );
if ( mFlags & FlagOptional )
code += QLatin1String( ", optional=True" );

if ( minimum() != std::numeric_limits<double>::lowest() + 1 )
code += QStringLiteral( ", minValue=%1" ).arg( minimum() );
if ( maximum() != std::numeric_limits<double>::max() )
code += QStringLiteral( ", maxValue=%1" ).arg( maximum() );
QgsProcessingContext c;
code += QStringLiteral( ", defaultValue=%1)" ).arg( valueAsPythonString( mDefault, c ) );
return code;
}
}
return QString();
}

QVariantMap QgsProcessingParameterDuration::toVariantMap() const
{
QVariantMap map = QgsProcessingParameterNumber::toVariantMap();
map.insert( QStringLiteral( "default_unit" ), static_cast< int >( mDefaultUnit ) );
return map;
}

bool QgsProcessingParameterDuration::fromVariantMap( const QVariantMap &map )
{
QgsProcessingParameterNumber::fromVariantMap( map );
mDefaultUnit = static_cast< QgsUnitTypes::TemporalUnit>( map.value( QStringLiteral( "default_unit" ), QgsUnitTypes::TemporalDays ).toInt() );
return true;
}


//
// QgsProcessingParameterScale
//
Expand Down
55 changes: 55 additions & 0 deletions src/core/processing/qgsprocessingparameters.h
Expand Up @@ -364,6 +364,8 @@ class CORE_EXPORT QgsProcessingParameterDefinition
sipType = sipType_QgsProcessingParameterNumber;
else if ( sipCpp->type() == QgsProcessingParameterDistance::typeName() )
sipType = sipType_QgsProcessingParameterDistance;
else if ( sipCpp->type() == QgsProcessingParameterDuration::typeName() )
sipType = sipType_QgsProcessingParameterDuration;
else if ( sipCpp->type() == QgsProcessingParameterScale::typeName() )
sipType = sipType_QgsProcessingParameterScale;
else if ( sipCpp->type() == QgsProcessingParameterRange::typeName() )
Expand Down Expand Up @@ -2235,6 +2237,59 @@ class CORE_EXPORT QgsProcessingParameterDistance : public QgsProcessingParameter

};

/**
* \class QgsProcessingParameterDuration
* \ingroup core
* \brief A double numeric parameter for duration values. The returned
* value will always be in milliseconds.
* \since QGIS 3.22
*/
class CORE_EXPORT QgsProcessingParameterDuration : public QgsProcessingParameterNumber
{
public:

/**
* Constructor for QgsProcessingParameterDuration.
*/
explicit QgsProcessingParameterDuration( const QString &name, const QString &description = QString(),
const QVariant &defaultValue = QVariant(),
bool optional = false,
double minValue = std::numeric_limits<double>::lowest() + 1,
double maxValue = std::numeric_limits<double>::max() );

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

QgsProcessingParameterDuration *clone() const override SIP_FACTORY;

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

/**
* Returns the default duration unit for the parameter.
*
* \see setDefaultUnit()
*/
QgsUnitTypes::TemporalUnit defaultUnit() const { return mDefaultUnit; }

/**
* Sets the default duration \a unit for the parameter.
*
* \see defaultUnit()
*/
void setDefaultUnit( QgsUnitTypes::TemporalUnit unit ) { mDefaultUnit = unit; }

QVariantMap toVariantMap() const override;
bool fromVariantMap( const QVariantMap &map ) override;

private:

QgsUnitTypes::TemporalUnit mDefaultUnit = QgsUnitTypes::TemporalMilliseconds;

};

/**
* \class QgsProcessingParameterScale
* \ingroup core
Expand Down

0 comments on commit 52bac40

Please sign in to comment.