diff --git a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in index 1af9e7afe7e..54a05f1213e 100644 --- a/python/core/auto_generated/processing/qgsprocessingparameters.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingparameters.sip.in @@ -1080,6 +1080,14 @@ Returns the coordinate reference system associated with an extent parameter valu .. seealso:: :py:func:`parameterAsExtent` %End + static QgsCoordinateReferenceSystem parameterAsExtentCrs( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ); +%Docstring +Returns the coordinate reference system associated with an extent parameter value. + +.. seealso:: :py:func:`parameterAsExtent` +%End + + static QgsPointXY parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ); %Docstring diff --git a/python/gui/auto_generated/qgsextentwidget.sip.in b/python/gui/auto_generated/qgsextentwidget.sip.in index efac681b32b..eba2cbd29ee 100644 --- a/python/gui/auto_generated/qgsextentwidget.sip.in +++ b/python/gui/auto_generated/qgsextentwidget.sip.in @@ -219,6 +219,12 @@ Emitted when the widget's extent is changed. void validationChanged( bool valid ); %Docstring Emitted when the widget's validation state changes. +%End + + void toggleDialogVisibility( bool visible ); +%Docstring +Emitted when the parent dialog visibility must be changed (e.g. +to permit access to the map canvas) %End protected: diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 0d72828fb0a..2c113e6eff5 100755 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -405,6 +405,16 @@ class CrsWidgetWrapper(WidgetWrapper): class ExtentWidgetWrapper(WidgetWrapper): USE_MIN_COVERING_EXTENT = "[Use min covering extent]" + 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("ExtentWidgetWrapper is deprecated and will be removed in QGIS 4.0", DeprecationWarning) + def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): widget = ExtentSelectionPanel(self.dialog, self.parameterDefinition()) @@ -1819,6 +1829,7 @@ class WidgetWrapperFactory: # deprecated, moved to c++ wrapper = CrsWidgetWrapper elif param.type() == 'extent': + # deprecated, moved to c++ wrapper = ExtentWidgetWrapper elif param.type() == 'point': # deprecated, moved to c++ diff --git a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py index b3cafacd3d3..9a6ebbea5fd 100755 --- a/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py +++ b/python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py @@ -46,7 +46,6 @@ from qgis.core import (QgsApplication, QgsProcessingParameterDefinition, QgsProcessingParameterCrs, QgsProcessingParameterMapLayer, - QgsProcessingParameterExtent, QgsProcessingParameterMatrix, QgsProcessingParameterMultipleLayers, QgsProcessingParameterNumber, @@ -487,9 +486,6 @@ class ModelerParameterDefinitionDialog(QDialog): QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return - elif (self.paramType == parameters.PARAMETER_EXTENT - or isinstance(self.param, QgsProcessingParameterExtent)): - self.param = QgsProcessingParameterExtent(name, description) elif (self.paramType == parameters.PARAMETER_ENUM or isinstance(self.param, QgsProcessingParameterEnum)): self.param = QgsProcessingParameterEnum(name, description, self.widget.options(), self.widget.allowMultiple(), self.widget.defaultOptions()) diff --git a/src/core/processing/qgsprocessingparameters.cpp b/src/core/processing/qgsprocessingparameters.cpp index 5a2f6c17c4d..58cf7e2fd6f 100644 --- a/src/core/processing/qgsprocessingparameters.cpp +++ b/src/core/processing/qgsprocessingparameters.cpp @@ -1181,7 +1181,12 @@ QgsGeometry QgsProcessingParameters::parameterAsExtentGeometry( const QgsProcess QgsCoordinateReferenceSystem QgsProcessingParameters::parameterAsExtentCrs( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ) { QVariant val = parameters.value( definition->name() ); + return parameterAsExtentCrs( definition, val, context ); +} +QgsCoordinateReferenceSystem QgsProcessingParameters::parameterAsExtentCrs( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ) +{ + QVariant val = value; if ( val.canConvert< QgsReferencedRectangle >() ) { QgsReferencedRectangle rr = val.value(); diff --git a/src/core/processing/qgsprocessingparameters.h b/src/core/processing/qgsprocessingparameters.h index 2b7ce277a04..7bcaabebccd 100644 --- a/src/core/processing/qgsprocessingparameters.h +++ b/src/core/processing/qgsprocessingparameters.h @@ -1155,6 +1155,14 @@ class CORE_EXPORT QgsProcessingParameters */ static QgsCoordinateReferenceSystem parameterAsExtentCrs( const QgsProcessingParameterDefinition *definition, const QVariantMap ¶meters, QgsProcessingContext &context ); + /** + * Returns the coordinate reference system associated with an extent parameter value. + * + * \see parameterAsExtent() + */ + static QgsCoordinateReferenceSystem parameterAsExtentCrs( const QgsProcessingParameterDefinition *definition, const QVariant &value, QgsProcessingContext &context ); + + /** * Evaluates the parameter with matching \a definition to a point. * diff --git a/src/gui/processing/qgsprocessingguiregistry.cpp b/src/gui/processing/qgsprocessingguiregistry.cpp index 523b744765e..ee06ba2b6f0 100644 --- a/src/gui/processing/qgsprocessingguiregistry.cpp +++ b/src/gui/processing/qgsprocessingguiregistry.cpp @@ -50,6 +50,7 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry() addParameterWidgetFactory( new QgsProcessingProviderConnectionWidgetWrapper() ); addParameterWidgetFactory( new QgsProcessingDatabaseSchemaWidgetWrapper() ); addParameterWidgetFactory( new QgsProcessingDatabaseTableWidgetWrapper() ); + addParameterWidgetFactory( new QgsProcessingExtentWidgetWrapper() ); } QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry() diff --git a/src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp b/src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp index 85df8709292..f04d0a3515f 100644 --- a/src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp +++ b/src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp @@ -51,6 +51,7 @@ #include "qgsproviderconnectioncombobox.h" #include "qgsdatabaseschemacombobox.h" #include "qgsdatabasetablecombobox.h" +#include "qgsextentwidget.h" #include #include #include @@ -4716,6 +4717,194 @@ void QgsProcessingDatabaseTableWidgetWrapper::postInitialize( const QListsetMargin( 0 ); + vlayout->setContentsMargins( 0, 0, 0, 0 ); + + vlayout->addWidget( new QLabel( tr( "Default value" ) ) ); + + mDefaultWidget = new QgsExtentWidget(); + mDefaultWidget->setNullValueAllowed( true, tr( "Not set" ) ); + if ( const QgsProcessingParameterExtent *extentParam = dynamic_cast( definition ) ) + { + if ( extentParam->defaultValue().isValid() ) + { + QgsRectangle rect = QgsProcessingParameters::parameterAsExtent( extentParam, extentParam->defaultValue(), context ); + QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsExtentCrs( extentParam, extentParam->defaultValue(), context ); + mDefaultWidget->setCurrentExtent( rect, crs ); + mDefaultWidget->setOutputExtentFromCurrent(); + } + else + { + mDefaultWidget->clear(); + } + } + + vlayout->addWidget( mDefaultWidget ); + setLayout( vlayout ); +} + +QgsProcessingParameterDefinition *QgsProcessingExtentParameterDefinitionWidget::createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const +{ + const QString defaultVal = mDefaultWidget->isValid() ? QStringLiteral( "%1,%2,%3,%4%5" ).arg( + QString::number( mDefaultWidget->outputExtent().xMinimum(), 'f', 9 ), + QString::number( mDefaultWidget->outputExtent().xMaximum(), 'f', 9 ), + QString::number( mDefaultWidget->outputExtent().yMinimum(), 'f', 9 ), + QString::number( mDefaultWidget->outputExtent().yMaximum(), 'f', 9 ), + mDefaultWidget->outputCrs().isValid() ? QStringLiteral( " [%1]" ).arg( mDefaultWidget->outputCrs().authid() ) : QString() + ) : QString(); + auto param = qgis::make_unique< QgsProcessingParameterExtent >( name, description, !defaultVal.isEmpty() ? QVariant( defaultVal ) : QVariant() ); + param->setFlags( flags ); + return param.release(); +} + + + +QgsProcessingExtentWidgetWrapper::QgsProcessingExtentWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent ) + : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent ) +{ + +} + +QWidget *QgsProcessingExtentWidgetWrapper::createWidget() +{ + const QgsProcessingParameterExtent *extentParam = dynamic_cast< const QgsProcessingParameterExtent *>( parameterDefinition() ); + switch ( type() ) + { + case QgsProcessingGui::Standard: + case QgsProcessingGui::Batch: + case QgsProcessingGui::Modeler: + { + mExtentWidget = new QgsExtentWidget( nullptr ); + if ( widgetContext().mapCanvas() ) + mExtentWidget->setMapCanvas( widgetContext().mapCanvas() ); + + if ( extentParam->flags() & QgsProcessingParameterDefinition::FlagOptional ) + mExtentWidget->setNullValueAllowed( true, tr( "Not set" ) ); + + mExtentWidget->setToolTip( parameterDefinition()->toolTip() ); + + connect( mExtentWidget, &QgsExtentWidget::extentChanged, this, [ = ] + { + emit widgetValueHasChanged( this ); + } ); + + if ( mDialog && type() != QgsProcessingGui::Modeler ) + setDialog( mDialog ); // setup connections to panel - dialog was previously set before the widget was created + + return mExtentWidget; + } + } + return nullptr; +} + +void QgsProcessingExtentWidgetWrapper::setWidgetContext( const QgsProcessingParameterWidgetContext &context ) +{ + QgsAbstractProcessingParameterWidgetWrapper::setWidgetContext( context ); + if ( mExtentWidget && context.mapCanvas() && type() != QgsProcessingGui::Modeler ) + mExtentWidget->setMapCanvas( context.mapCanvas() ); +} + +void QgsProcessingExtentWidgetWrapper::setDialog( QDialog *dialog ) +{ + mDialog = dialog; + if ( mExtentWidget && mDialog && type() != QgsProcessingGui::Modeler ) + { + connect( mExtentWidget, &QgsExtentWidget::toggleDialogVisibility, mDialog, [ = ]( bool visible ) + { + if ( !visible ) + mDialog->showMinimized(); + else + { + mDialog->showNormal(); + mDialog->raise(); + mDialog->activateWindow(); + } + } ); + } + QgsAbstractProcessingParameterWidgetWrapper::setDialog( dialog ); +} + +void QgsProcessingExtentWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context ) +{ + if ( mExtentWidget ) + { + if ( !value.isValid() || ( value.type() == QVariant::String && value.toString().isEmpty() ) ) + mExtentWidget->clear(); + else + { + QgsRectangle r = QgsProcessingParameters::parameterAsExtent( parameterDefinition(), value, context ); + QgsCoordinateReferenceSystem crs = QgsProcessingParameters::parameterAsPointCrs( parameterDefinition(), value, context ); + mExtentWidget->setCurrentExtent( r, crs ); + mExtentWidget->setOutputExtentFromCurrent(); + } + } +} + +QVariant QgsProcessingExtentWidgetWrapper::widgetValue() const +{ + if ( mExtentWidget ) + { + const QString val = mExtentWidget->isValid() ? QStringLiteral( "%1,%2,%3,%4%5" ).arg( + QString::number( mExtentWidget->outputExtent().xMinimum(), 'f', 9 ), + QString::number( mExtentWidget->outputExtent().xMaximum(), 'f', 9 ), + QString::number( mExtentWidget->outputExtent().yMinimum(), 'f', 9 ), + QString::number( mExtentWidget->outputExtent().yMaximum(), 'f', 9 ), + mExtentWidget->outputCrs().isValid() ? QStringLiteral( " [%1]" ).arg( mExtentWidget->outputCrs().authid() ) : QString() + ) : QString(); + + return val.isEmpty() ? QVariant() : QVariant( val ); + } + else + return QVariant(); +} + +QStringList QgsProcessingExtentWidgetWrapper::compatibleParameterTypes() const +{ + return QStringList() + << QgsProcessingParameterExtent::typeName() + << QgsProcessingParameterString::typeName(); +} + +QStringList QgsProcessingExtentWidgetWrapper::compatibleOutputTypes() const +{ + return QStringList() + << QgsProcessingOutputString::typeName(); +} + +QList QgsProcessingExtentWidgetWrapper::compatibleDataTypes() const +{ + return QList(); +} + +QString QgsProcessingExtentWidgetWrapper::modelerExpressionFormatString() const +{ + return tr( "string of the format 'x min,x max,y min,y max' or a geometry value (bounding box is used)" ); +} + +QString QgsProcessingExtentWidgetWrapper::parameterType() const +{ + return QgsProcessingParameterExtent::typeName(); +} + +QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExtentWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) +{ + return new QgsProcessingExtentWidgetWrapper( parameter, type ); +} + +QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingExtentWidgetWrapper::createParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm ) +{ + return new QgsProcessingExtentParameterDefinitionWidget( context, widgetContext, definition, algorithm ); +} + ///@endcond PRIVATE + diff --git a/src/gui/processing/qgsprocessingwidgetwrapperimpl.h b/src/gui/processing/qgsprocessingwidgetwrapperimpl.h index 5446e5eddd1..e8bbdafc048 100644 --- a/src/gui/processing/qgsprocessingwidgetwrapperimpl.h +++ b/src/gui/processing/qgsprocessingwidgetwrapperimpl.h @@ -57,6 +57,7 @@ class QgsTimeEdit; class QgsProviderConnectionComboBox; class QgsDatabaseSchemaComboBox; class QgsDatabaseTableComboBox; +class QgsExtentWidget; ///@cond PRIVATE @@ -894,6 +895,67 @@ class GUI_EXPORT QgsProcessingPointWidgetWrapper : public QgsAbstractProcessingP }; + + +class GUI_EXPORT QgsProcessingExtentParameterDefinitionWidget : public QgsProcessingAbstractParameterDefinitionWidget +{ + Q_OBJECT + public: + + QgsProcessingExtentParameterDefinitionWidget( QgsProcessingContext &context, + const QgsProcessingParameterWidgetContext &widgetContext, + const QgsProcessingParameterDefinition *definition = nullptr, + const QgsProcessingAlgorithm *algorithm = nullptr, QWidget *parent SIP_TRANSFERTHIS = nullptr ); + QgsProcessingParameterDefinition *createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const override; + + private: + + QgsExtentWidget *mDefaultWidget = nullptr; + +}; + +class GUI_EXPORT QgsProcessingExtentWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface +{ + Q_OBJECT + + public: + + QgsProcessingExtentWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr, + QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard, QWidget *parent = nullptr ); + + // QgsProcessingParameterWidgetFactoryInterface + QString parameterType() const override; + QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) override; + QgsProcessingAbstractParameterDefinitionWidget *createParameterDefinitionWidget( + QgsProcessingContext &context, + const QgsProcessingParameterWidgetContext &widgetContext, + const QgsProcessingParameterDefinition *definition = nullptr, + const QgsProcessingAlgorithm *algorithm = nullptr ) override; + + // QgsProcessingParameterWidgetWrapper interface + QWidget *createWidget() override SIP_FACTORY; + void setWidgetContext( const QgsProcessingParameterWidgetContext &context ) override; + void setDialog( QDialog *dialog ) override; + + protected: + + void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override; + QVariant widgetValue() const override; + + QStringList compatibleParameterTypes() const override; + + QStringList compatibleOutputTypes() const override; + + QList< int > compatibleDataTypes() const override; + QString modelerExpressionFormatString() const override; + private: + + QgsExtentWidget *mExtentWidget = nullptr; + QDialog *mDialog = nullptr; + + friend class TestProcessingGui; +}; + class GUI_EXPORT QgsProcessingColorParameterDefinitionWidget : public QgsProcessingAbstractParameterDefinitionWidget { Q_OBJECT diff --git a/src/gui/qgsextentgroupbox.cpp b/src/gui/qgsextentgroupbox.cpp index 1914848d92e..9131f95cf81 100644 --- a/src/gui/qgsextentgroupbox.cpp +++ b/src/gui/qgsextentgroupbox.cpp @@ -28,6 +28,11 @@ QgsExtentGroupBox::QgsExtentGroupBox( QWidget *parent ) connect( this, &QGroupBox::toggled, this, &QgsExtentGroupBox::groupBoxClicked ); connect( mWidget, &QgsExtentWidget::extentChanged, this, &QgsExtentGroupBox::widgetExtentChanged ); connect( mWidget, &QgsExtentWidget::validationChanged, this, &QgsExtentGroupBox::validationChanged ); + + connect( mWidget, &QgsExtentWidget::toggleDialogVisibility, this, [ = ]( bool visible ) + { + window()->setVisible( visible ); + } ); } void QgsExtentGroupBox::setOriginalExtent( const QgsRectangle &originalExtent, const QgsCoordinateReferenceSystem &originalCrs ) diff --git a/src/gui/qgsextentwidget.cpp b/src/gui/qgsextentwidget.cpp index 841d5277547..393a99d6c1a 100644 --- a/src/gui/qgsextentwidget.cpp +++ b/src/gui/qgsextentwidget.cpp @@ -259,6 +259,7 @@ void QgsExtentWidget::clear() whileBlocking( mXMaxLineEdit )->clear(); whileBlocking( mYMinLineEdit )->clear(); whileBlocking( mYMaxLineEdit )->clear(); + whileBlocking( mCondensedLineEdit )->clearValue(); setValid( false ); if ( prevWasNull ) @@ -388,13 +389,14 @@ void QgsExtentWidget::setOutputExtentFromDrawOnCanvas() connect( mMapToolExtent.get(), &QgsMapToolExtent::extentChanged, this, &QgsExtentWidget::extentDrawn ); connect( mMapToolExtent.get(), &QgsMapTool::deactivated, this, [ = ] { - window()->setVisible( true ); + emit toggleDialogVisibility( true ); mMapToolPrevious = nullptr; } ); } mMapToolExtent->setRatio( mRatio ); mCanvas->setMapTool( mMapToolExtent.get() ); - window()->setVisible( false ); + + emit toggleDialogVisibility( false ); } } @@ -402,7 +404,7 @@ void QgsExtentWidget::extentDrawn( const QgsRectangle &extent ) { setOutputExtent( extent, mCanvas->mapSettings().destinationCrs(), DrawOnCanvas ); mCanvas->setMapTool( mMapToolPrevious ); - window()->setVisible( true ); + emit toggleDialogVisibility( true ); mMapToolPrevious = nullptr; } diff --git a/src/gui/qgsextentwidget.h b/src/gui/qgsextentwidget.h index bdb52dd0308..e925dfdd30c 100644 --- a/src/gui/qgsextentwidget.h +++ b/src/gui/qgsextentwidget.h @@ -226,6 +226,12 @@ class GUI_EXPORT QgsExtentWidget : public QWidget, private Ui::QgsExtentGroupBox */ void validationChanged( bool valid ); + /** + * Emitted when the parent dialog visibility must be changed (e.g. + * to permit access to the map canvas) + */ + void toggleDialogVisibility( bool visible ); + protected: void dragEnterEvent( QDragEnterEvent *event ) override; diff --git a/tests/src/gui/testprocessinggui.cpp b/tests/src/gui/testprocessinggui.cpp index 59671306e7e..77a69910a1e 100644 --- a/tests/src/gui/testprocessinggui.cpp +++ b/tests/src/gui/testprocessinggui.cpp @@ -77,6 +77,7 @@ #include "qgsprocessingoutputdestinationwidget.h" #include "qgssettings.h" #include "qgsprocessingfeaturesourceoptionswidget.h" +#include "qgsextentwidget.h" class TestParamType : public QgsProcessingParameterDefinition { @@ -203,6 +204,7 @@ class TestProcessingGui : public QObject void testLayoutItemWrapper(); void testPointPanel(); void testPointWrapper(); + void testExtentWrapper(); void testColorWrapper(); void testCoordinateOperationWrapper(); void mapLayerComboBox(); @@ -3705,6 +3707,127 @@ void TestProcessingGui::testPointWrapper() } +void TestProcessingGui::testExtentWrapper() +{ + auto testWrapper = []( QgsProcessingGui::WidgetType type ) + { + // non optional + QgsProcessingParameterExtent param( QStringLiteral( "extent" ), QStringLiteral( "extent" ), false ); + + QgsProcessingExtentWidgetWrapper wrapper( ¶m, type ); + + QgsProcessingContext context; + QWidget *w = wrapper.createWrappedWidget( context ); + + QSignalSpy spy( &wrapper, &QgsProcessingExtentWidgetWrapper::widgetValueHasChanged ); + wrapper.setWidgetValue( "1,2,3,4", context ); + QCOMPARE( spy.count(), 1 ); + QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "1.000000000,2.000000000,3.000000000,4.000000000" ) ); + QCOMPARE( static_cast< QgsExtentWidget * >( wrapper.wrappedWidget() )->outputExtent(), QgsRectangle( 1, 3, 2, 4 ) ); + + wrapper.setWidgetValue( "1,2,3,4 [EPSG:3111]", context ); + QCOMPARE( spy.count(), 2 ); + QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "1.000000000,2.000000000,3.000000000,4.000000000 [EPSG:3111]" ) ); + QCOMPARE( static_cast< QgsExtentWidget * >( wrapper.wrappedWidget() )->outputExtent(), QgsRectangle( 1, 3, 2, 4 ) ); + QCOMPARE( static_cast< QgsExtentWidget * >( wrapper.wrappedWidget() )->outputCrs().authid(), QStringLiteral( "EPSG:3111" ) ); + + // check signal + static_cast< QgsExtentWidget * >( wrapper.wrappedWidget() )->setOutputExtentFromUser( QgsRectangle( 11, 22, 33, 44 ), QgsCoordinateReferenceSystem() ); + QCOMPARE( spy.count(), 3 ); + + QLabel *l = wrapper.createWrappedLabel(); + if ( wrapper.type() != QgsProcessingGui::Batch ) + { + QVERIFY( l ); + QCOMPARE( l->text(), QStringLiteral( "extent" ) ); + QCOMPARE( l->toolTip(), param.toolTip() ); + delete l; + } + else + { + QVERIFY( !l ); + } + + delete w; + + // optional + + QgsProcessingParameterExtent param2( QStringLiteral( "extent" ), QStringLiteral( "extent" ), QVariant(), true ); + + QgsProcessingExtentWidgetWrapper wrapper2( ¶m2, type ); + w = wrapper2.createWrappedWidget( context ); + + QSignalSpy spy2( &wrapper2, &QgsProcessingExtentWidgetWrapper::widgetValueHasChanged ); + wrapper2.setWidgetValue( "1,2,3,4", context ); + QCOMPARE( spy2.count(), 1 ); + QCOMPARE( static_cast< QgsExtentWidget * >( wrapper2.wrappedWidget() )->outputExtent(), QgsRectangle( 1, 3, 2, 4 ) ); + QCOMPARE( wrapper2.widgetValue().toString(), QStringLiteral( "1.000000000,2.000000000,3.000000000,4.000000000" ) ); + + wrapper2.setWidgetValue( "1,2,3,4 [EPSG:3111]", context ); + QCOMPARE( spy2.count(), 2 ); + QCOMPARE( wrapper2.widgetValue().toString(), QStringLiteral( "1.000000000,2.000000000,3.000000000,4.000000000 [EPSG:3111]" ) ); + QCOMPARE( static_cast< QgsExtentWidget * >( wrapper2.wrappedWidget() )->outputExtent(), QgsRectangle( 1, 3, 2, 4 ) ); + QCOMPARE( static_cast< QgsExtentWidget * >( wrapper2.wrappedWidget() )->outputCrs().authid(), QStringLiteral( "EPSG:3111" ) ); + wrapper2.setWidgetValue( QVariant(), context ); + QCOMPARE( spy2.count(), 3 ); + QVERIFY( !wrapper2.widgetValue().isValid() ); + QVERIFY( !static_cast< QgsExtentWidget * >( wrapper2.wrappedWidget() )->isValid() ); + + wrapper2.setWidgetValue( "1,3,4,7", context ); + QCOMPARE( spy2.count(), 4 ); + wrapper2.setWidgetValue( "", context ); + QCOMPARE( spy2.count(), 5 ); + QVERIFY( !wrapper2.widgetValue().isValid() ); + QVERIFY( !static_cast< QgsExtentWidget * >( wrapper2.wrappedWidget() )->isValid() ); + + // check signals + wrapper2.setWidgetValue( "1,3,9,8", context ); + QCOMPARE( spy2.count(), 6 ); + static_cast< QgsExtentWidget * >( wrapper2.wrappedWidget() )->clear(); + QCOMPARE( spy2.count(), 7 ); + + delete w; + + }; + + // standard wrapper + testWrapper( QgsProcessingGui::Standard ); + + // batch wrapper + testWrapper( QgsProcessingGui::Batch ); + + // modeler wrapper + testWrapper( QgsProcessingGui::Modeler ); + + // config widget + QgsProcessingContext context; + QgsProcessingParameterWidgetContext widgetContext; + std::unique_ptr< QgsProcessingParameterDefinitionWidget > widget = qgis::make_unique< QgsProcessingParameterDefinitionWidget >( QStringLiteral( "extent" ), context, widgetContext ); + std::unique_ptr< QgsProcessingParameterDefinition > def( widget->createParameter( QStringLiteral( "param_name" ) ) ); + QCOMPARE( def->name(), QStringLiteral( "param_name" ) ); + QVERIFY( !( def->flags() & QgsProcessingParameterDefinition::FlagOptional ) ); // should default to mandatory + QVERIFY( !( def->flags() & QgsProcessingParameterDefinition::FlagAdvanced ) ); + + // using a parameter definition as initial values + QgsProcessingParameterExtent extentParam( QStringLiteral( "n" ), QStringLiteral( "test desc" ), QStringLiteral( "1,2,3,4" ) ); + widget = qgis::make_unique< QgsProcessingParameterDefinitionWidget >( QStringLiteral( "extent" ), context, widgetContext, &extentParam ); + def.reset( widget->createParameter( QStringLiteral( "param_name" ) ) ); + QCOMPARE( def->name(), QStringLiteral( "param_name" ) ); + QCOMPARE( def->description(), QStringLiteral( "test desc" ) ); + QVERIFY( !( def->flags() & QgsProcessingParameterDefinition::FlagOptional ) ); + QVERIFY( !( def->flags() & QgsProcessingParameterDefinition::FlagAdvanced ) ); + QCOMPARE( static_cast< QgsProcessingParameterExtent * >( def.get() )->defaultValue().toString(), QStringLiteral( "1.000000000,2.000000000,3.000000000,4.000000000" ) ); + extentParam.setFlags( QgsProcessingParameterDefinition::FlagAdvanced | QgsProcessingParameterDefinition::FlagOptional ); + extentParam.setDefaultValue( QStringLiteral( "4,7,8,9" ) ); + widget = qgis::make_unique< QgsProcessingParameterDefinitionWidget >( QStringLiteral( "extent" ), context, widgetContext, &extentParam ); + def.reset( widget->createParameter( QStringLiteral( "param_name" ) ) ); + QCOMPARE( def->name(), QStringLiteral( "param_name" ) ); + QCOMPARE( def->description(), QStringLiteral( "test desc" ) ); + QVERIFY( def->flags() & QgsProcessingParameterDefinition::FlagOptional ); + QVERIFY( def->flags() & QgsProcessingParameterDefinition::FlagAdvanced ); + QCOMPARE( static_cast< QgsProcessingParameterExtent * >( def.get() )->defaultValue().toString(), QStringLiteral( "4.000000000,7.000000000,8.000000000,9.000000000" ) ); +} + void TestProcessingGui::testColorWrapper() { auto testWrapper = []( QgsProcessingGui::WidgetType type )