[FEATURE][processing] Port output parameter wrappers to new c++ API for modeler

This allows a range of new possibilities, including:
- models with static outputs for child algorithms, e.g. always saving
a child algorithm's output to a geopackage or postgres layer
- models with expression based output values for child algorithms, e.g.
generating an automatic file name based on today's date and saving
outputs to that file
This commit is contained in:
Nyall Dawson 2020-03-31 22:47:57 +10:00
parent 3d9bd71707
commit d20a3bddc0
11 changed files with 619 additions and 53 deletions

View File

@ -30,6 +30,7 @@ Source for the value of a parameter for a child algorithm within a model.
StaticValue,
Expression,
ExpressionText,
ModelOutput,
};
QgsProcessingModelChildParameterSource();

View File

@ -91,7 +91,7 @@ from QgsProcessing.SourceType.
void setExpressionHelpText( const QString &text );
%Docstring
Set the expected expression format ``text``, which is shown in the expression builder dialog for the widget
when in the "pre-calculated" expression mode. This is purely a text format and no expression validation is made
when in the "pre-calculated" ex pression mode. This is purely a text format and no expression validation is made
against it.
%End
@ -120,6 +120,39 @@ Sets the current ``values`` for the parameter.
.. seealso:: :py:func:`value`
.. versionadded:: 3.14
%End
void setToModelOutput( const QString &value );
%Docstring
Sets the widget to a model output, for destination parameters only.
.. seealso:: :py:func:`isModelOutput`
.. seealso:: :py:func:`modelOutputName`
.. versionadded:: 3.14
%End
bool isModelOutput() const;
%Docstring
Returns ``True`` if the widget is set to the model output mode.
.. seealso:: :py:func:`setToModelOutput`
.. seealso:: :py:func:`modelOutputName`
.. versionadded:: 3.14
%End
QString modelOutputName() const;
%Docstring
Returns the model output name, if isModelOutput() is ``True``.
.. seealso:: :py:func:`setToModelOutput`
.. seealso:: :py:func:`isModelOutput`
.. versionadded:: 3.14
%End

View File

@ -35,11 +35,6 @@ from qgis.core import (Qgis,
QgsProcessingModelOutput,
QgsProcessingModelChildAlgorithm,
QgsProcessingModelChildParameterSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterFileDestination,
QgsProcessingParameterFolderDestination,
QgsProcessingParameterVectorDestination,
QgsProcessingOutputDefinition)
from qgis.gui import (QgsGui,
@ -175,11 +170,8 @@ class ModelerParametersPanelWidget(QgsPanelWidget):
return self._alg
def setupUi(self):
self.checkBoxes = {}
self.showAdvanced = False
self.wrappers = {}
self.valueItems = {}
self.dependentItems = {}
self.algorithmItem = None
self.mainLayout = QVBoxLayout()
@ -246,8 +238,6 @@ class ModelerParametersPanelWidget(QgsPanelWidget):
else:
widget = wrapper.widget
if widget is not None:
self.valueItems[param.name()] = widget
if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
label = wrapper.createLabel()
else:
@ -263,19 +253,29 @@ class ModelerParametersPanelWidget(QgsPanelWidget):
self.verticalLayout.addWidget(label)
self.verticalLayout.addWidget(widget)
for dest in self._alg.destinationParameterDefinitions():
if dest.flags() & QgsProcessingParameterDefinition.FlagHidden:
for output in self._alg.destinationParameterDefinitions():
if output.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination,
QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination,
QgsProcessingParameterFolderDestination)):
label = QLabel(dest.description())
item = QgsFilterLineEdit()
if hasattr(item, 'setPlaceholderText'):
item.setPlaceholderText(self.tr('[Enter name if this is a final result]'))
widget = QgsGui.processingGuiRegistry().createModelerParameterWidget(self.model,
self.childId,
output,
self.context)
widget.setDialog(self.dialog)
widget.setWidgetContext(widget_context)
widget.registerProcessingContextGenerator(self.context_generator)
self.wrappers[output.name()] = widget
item = QgsFilterLineEdit()
if hasattr(item, 'setPlaceholderText'):
item.setPlaceholderText(self.tr('[Enter name if this is a final result]'))
label = widget.createLabel()
if label is not None:
self.verticalLayout.addWidget(label)
self.verticalLayout.addWidget(item)
self.valueItems[dest.name()] = item
self.verticalLayout.addWidget(widget)
label = QLabel(' ')
self.verticalLayout.addWidget(label)
@ -401,9 +401,34 @@ class ModelerParametersPanelWidget(QgsPanelWidget):
value = value.staticValue()
wrapper.setValue(value)
for name, out in alg.modelOutputs().items():
if out.childOutputName() in self.valueItems:
self.valueItems[out.childOutputName()].setText(out.name())
for output in self.algorithm().destinationParameterDefinitions():
if output.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
model_output_name = None
for name, out in alg.modelOutputs().items():
if out.childId() == self.childId and out.childOutputName() == output.name():
# this destination parameter is linked to a model output
model_output_name = out.name()
break
value = None
if model_output_name is None and output.name() in alg.parameterSources():
value = alg.parameterSources()[output.name()]
if isinstance(value, list) and len(value) == 1:
value = value[0]
elif isinstance(value, list) and len(value) == 0:
value = None
wrapper = self.wrappers[output.name()]
if model_output_name is not None:
wrapper.setToModelOutput(model_output_name)
elif value is not None or output.defaultValue() is not None:
if value is None:
value = QgsProcessingModelChildParameterSource.fromStaticValue(output.defaultValue())
wrapper.setWidgetValue(value)
selected = []
dependencies = self.getAvailableDependencies()
@ -457,21 +482,31 @@ class ModelerParametersPanelWidget(QgsPanelWidget):
alg.addParameterSources(param.name(), val)
outputs = {}
for dest in self._alg.destinationParameterDefinitions():
if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden:
name = self.valueItems[dest.name()].text()
if name.strip() != '':
output = QgsProcessingModelOutput(name, name)
output.setChildId(alg.childId())
output.setChildOutputName(dest.name())
outputs[name] = output
for output in self._alg.destinationParameterDefinitions():
if not output.flags() & QgsProcessingParameterDefinition.FlagHidden:
wrapper = self.wrappers[output.name()]
if dest.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput:
if dest.name() not in outputs:
output = QgsProcessingModelOutput(dest.name(), dest.name())
output.setChildId(alg.childId())
output.setChildOutputName(dest.name())
outputs[dest.name()] = output
if wrapper.isModelOutput():
name = wrapper.modelOutputName()
if name:
model_output = QgsProcessingModelOutput(name, name)
model_output.setChildId(alg.childId())
model_output.setChildOutputName(output.name())
outputs[name] = model_output
else:
val = wrapper.value()
if isinstance(val, QgsProcessingModelChildParameterSource):
val = [val]
alg.addParameterSources(output.name(), val)
if output.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput:
if output.name() not in outputs:
model_output = QgsProcessingModelOutput(output.name(), output.name())
model_output.setChildId(alg.childId())
model_output.setChildOutputName(output.name())
outputs[output.name()] = model_output
alg.setModelOutputs(outputs)
@ -521,12 +556,6 @@ class ModelerParametersWidget(QWidget):
self.commentEdit.selectAll()
def setupUi(self):
self.showAdvanced = False
self.wrappers = {}
self.valueItems = {}
self.dependentItems = {}
self.algorithmItem = None
self.mainLayout = QVBoxLayout()
self.mainLayout.setContentsMargins(0, 0, 0, 0)
self.tab = QTabWidget()

View File

@ -136,6 +136,9 @@ QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const QgsP
expressionText = QgsExpression::replaceExpressionText( source.expressionText(), &expressionContext );
break;
}
case QgsProcessingModelChildParameterSource::ModelOutput:
break;
}
}
@ -856,6 +859,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
case QgsProcessingModelChildParameterSource::Expression:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ModelOutput:
continue;
};
variables.insert( safeName( name ), VariableDefinition( value, source, description ) );
@ -901,6 +905,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
case QgsProcessingModelChildParameterSource::Expression:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ModelOutput:
continue;
};
@ -959,6 +964,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
case QgsProcessingModelChildParameterSource::Expression:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ModelOutput:
continue;
};

View File

@ -39,6 +39,8 @@ bool QgsProcessingModelChildParameterSource::operator==( const QgsProcessingMode
return mExpression == other.mExpression;
case ExpressionText:
return mExpressionText == other.mExpressionText;
case ModelOutput:
return true;
}
return false;
}
@ -120,6 +122,9 @@ QVariant QgsProcessingModelChildParameterSource::toVariant() const
case ExpressionText:
map.insert( QStringLiteral( "expression_text" ), mExpressionText );
break;
case ModelOutput:
break;
}
return map;
}
@ -149,6 +154,9 @@ bool QgsProcessingModelChildParameterSource::loadVariant( const QVariantMap &map
case ExpressionText:
mExpressionText = map.value( QStringLiteral( "expression_text" ) ).toString();
break;
case ModelOutput:
break;
}
return true;
}
@ -179,6 +187,9 @@ QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessin
case ExpressionText:
return mExpressionText;
case ModelOutput:
return QString();
}
return QString();
}
@ -222,6 +233,9 @@ QString QgsProcessingModelChildParameterSource::friendlyIdentifier( QgsProcessin
case ExpressionText:
return mExpressionText;
case ModelOutput:
return QString();
}
return QString();
}

View File

@ -43,6 +43,7 @@ class CORE_EXPORT QgsProcessingModelChildParameterSource
StaticValue, //!< Parameter value is a static value
Expression, //!< Parameter value is taken from an expression, evaluated just before the algorithm runs
ExpressionText, //!< Parameter value is taken from a text with expressions, evaluated just before the algorithm runs
ModelOutput, //!< Parameter value is linked to an output parameter for the model
};
/**

View File

@ -395,6 +395,7 @@ QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForPa
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::ModelOutput:
break;
}
}

View File

@ -26,6 +26,7 @@
#include "qgsguiutils.h"
#include "qgsexpressioncontext.h"
#include "qgsapplication.h"
#include "qgsfilterlineedit.h"
#include <QHBoxLayout>
#include <QToolButton>
#include <QStackedWidget>
@ -108,12 +109,25 @@ QgsProcessingModelerParameterWidget::QgsProcessingModelerParameterWidget( QgsPro
hWidget3->setLayout( hLayout3 );
mStackedWidget->addWidget( hWidget3 );
if ( mParameterDefinition->isDestination() )
{
mModelOutputName = new QgsFilterLineEdit();
mModelOutputName->setPlaceholderText( tr( "[Enter name if this is a final result]" ) );
QHBoxLayout *hLayout4 = new QHBoxLayout();
hLayout4->setMargin( 0 );
hLayout4->setContentsMargins( 0, 0, 0, 0 );
hLayout4->addWidget( mModelOutputName );
QWidget *hWidget4 = new QWidget();
hWidget4->setLayout( hLayout4 );
mStackedWidget->addWidget( hWidget4 );
}
hLayout->setMargin( 0 );
hLayout->setContentsMargins( 0, 0, 0, 0 );
hLayout->addWidget( mStackedWidget, 1 );
setLayout( hLayout );
setSourceType( QgsProcessingModelChildParameterSource::StaticValue );
setSourceType( mParameterDefinition->isDestination() ? QgsProcessingModelChildParameterSource::ModelOutput : QgsProcessingModelChildParameterSource::StaticValue );
}
QgsProcessingModelerParameterWidget::~QgsProcessingModelerParameterWidget() = default;
@ -172,6 +186,23 @@ void QgsProcessingModelerParameterWidget::setWidgetValue( const QList<QgsProcess
}
}
void QgsProcessingModelerParameterWidget::setToModelOutput( const QString &value )
{
if ( mModelOutputName )
mModelOutputName->setText( value );
setSourceType( QgsProcessingModelChildParameterSource::ModelOutput );
}
bool QgsProcessingModelerParameterWidget::isModelOutput() const
{
return currentSourceType() == ModelOutput;
}
QString QgsProcessingModelerParameterWidget::modelOutputName() const
{
return mModelOutputName ? mModelOutputName->text().trimmed() : QString();
}
QVariant QgsProcessingModelerParameterWidget::value() const
{
switch ( currentSourceType() )
@ -205,6 +236,9 @@ QVariant QgsProcessingModelerParameterWidget::value() const
const QStringList parts = mChildOutputCombo->currentData().toStringList();
return QVariant::fromValue( QgsProcessingModelChildParameterSource::fromChildOutput( parts.value( 0, QString() ), parts.value( 1, QString() ) ) );
}
case ModelOutput:
return mModelOutputName ? ( mModelOutputName->text().trimmed().isEmpty() ? QVariant() : mModelOutputName->text() ) : QVariant();
}
return QVariant::fromValue( QgsProcessingModelChildParameterSource() );
@ -249,6 +283,14 @@ void QgsProcessingModelerParameterWidget::sourceMenuAboutToShow()
const SourceType currentSource = currentSourceType();
if ( mParameterDefinition->isDestination() )
{
QAction *modelOutputAction = mSourceMenu->addAction( tr( "Model Output" ) );
modelOutputAction->setCheckable( currentSource == ModelOutput );
modelOutputAction->setChecked( currentSource == ModelOutput );
modelOutputAction->setData( QgsProcessingModelChildParameterSource::ModelOutput );
}
if ( mHasStaticWrapper )
{
QAction *fixedValueAction = mSourceMenu->addAction( tr( "Value" ) );
@ -318,6 +360,14 @@ void QgsProcessingModelerParameterWidget::setSourceType( QgsProcessingModelChild
break;
}
case QgsProcessingModelChildParameterSource::ModelOutput:
{
mSourceButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconModelOutput.svg" ) ) );
mStackedWidget->setCurrentIndex( static_cast< int >( ModelOutput ) );
mSourceButton->setToolTip( tr( "Model Output" ) );
break;
}
case QgsProcessingModelChildParameterSource::ExpressionText:
break;
}
@ -372,6 +422,7 @@ void QgsProcessingModelerParameterWidget::populateSources( const QStringList &co
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::Expression:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::ModelOutput:
break;
}

View File

@ -32,6 +32,7 @@ class QgsExpressionLineEdit;
class QgsProcessingModelAlgorithm;
class QgsProcessingParameterWidgetContext;
class QgsProcessingContextGenerator;
class QgsFilterLineEdit;
class QLabel;
class QToolButton;
@ -117,7 +118,7 @@ class GUI_EXPORT QgsProcessingModelerParameterWidget : public QWidget, public Qg
/**
* Set the expected expression format \a text, which is shown in the expression builder dialog for the widget
* when in the "pre-calculated" expression mode. This is purely a text format and no expression validation is made
* when in the "pre-calculated" ex pression mode. This is purely a text format and no expression validation is made
* against it.
*/
void setExpressionHelpText( const QString &text );
@ -149,6 +150,33 @@ class GUI_EXPORT QgsProcessingModelerParameterWidget : public QWidget, public Qg
*/
void setWidgetValue( const QList< QgsProcessingModelChildParameterSource > &values );
/**
* Sets the widget to a model output, for destination parameters only.
*
* \see isModelOutput()
* \see modelOutputName()
* \since QGIS 3.14
*/
void setToModelOutput( const QString &value );
/**
* Returns TRUE if the widget is set to the model output mode.
*
* \see setToModelOutput()
* \see modelOutputName()
* \since QGIS 3.14
*/
bool isModelOutput() const;
/**
* Returns the model output name, if isModelOutput() is TRUE.
*
* \see setToModelOutput()
* \see isModelOutput()
* \since QGIS 3.14
*/
QString modelOutputName() const;
/**
* Returns the current value of the parameter.
*
@ -179,6 +207,7 @@ class GUI_EXPORT QgsProcessingModelerParameterWidget : public QWidget, public Qg
Expression = 1,
ModelParameter = 2,
ChildOutput = 3,
ModelOutput = 4,
};
SourceType currentSourceType() const;
@ -206,6 +235,7 @@ class GUI_EXPORT QgsProcessingModelerParameterWidget : public QWidget, public Qg
QgsExpressionLineEdit *mExpressionWidget = nullptr;
QComboBox *mModelInputCombo = nullptr;
QComboBox *mChildOutputCombo = nullptr;
QgsFilterLineEdit *mModelOutputName = nullptr;
friend class TestProcessingGui;
};

View File

@ -5891,6 +5891,7 @@ QWidget *QgsProcessingOutputWidgetWrapper::createWidget()
switch ( type() )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Modeler:
{
mOutputWidget = new QgsProcessingLayerOutputDestinationWidget( destParam, false );
mOutputWidget->setToolTip( parameterDefinition()->toolTip() );
@ -5903,17 +5904,16 @@ QWidget *QgsProcessingOutputWidgetWrapper::createWidget()
emit widgetValueHasChanged( this );
} );
if ( destParam->type() == QgsProcessingParameterRasterDestination::typeName() ||
destParam->type() == QgsProcessingParameterFeatureSink::typeName() ||
destParam->type() == QgsProcessingParameterVectorDestination::typeName() )
if ( type() == QgsProcessingGui::Standard
&& ( destParam->type() == QgsProcessingParameterRasterDestination::typeName() ||
destParam->type() == QgsProcessingParameterFeatureSink::typeName() ||
destParam->type() == QgsProcessingParameterVectorDestination::typeName() ) )
mOutputWidget->addOpenAfterRunningOption();
return mOutputWidget;
}
case QgsProcessingGui::Batch:
break;
case QgsProcessingGui::Modeler:
break;
}
return nullptr;

View File

@ -229,6 +229,11 @@ class TestProcessingGui : public QObject
void testOutputDefinitionWidgetFolder();
void testOutputDefinitionWidgetFileOut();
void testFeatureSourceOptionsWidget();
void testVectorOutWrapper();
void testSinkWrapper();
void testRasterOutWrapper();
void testFileOutWrapper();
void testFolderOutWrapper();
private:
@ -652,7 +657,8 @@ void TestProcessingGui::testModelerWrapper()
model.addModelParameter( new QgsProcessingParameterBoolean( "p1", "desc" ), bool1 );
QgsProcessingModelParameter testParam( "p2" );
model.addModelParameter( new TestParamType( "test_type", "p2" ), testParam );
QgsProcessingModelParameter testDestParam( "p3" );
model.addModelParameter( new QgsProcessingParameterFileDestination( "test_dest", "p3" ), testDestParam );
// try to create a parameter widget, no factories registered
QgsProcessingGuiRegistry registry;
QgsProcessingContext context;
@ -720,6 +726,25 @@ void TestProcessingGui::testModelerWrapper()
QCOMPARE( w->value().value< QgsProcessingModelChildParameterSource>().outputChildId(), QStringLiteral( "alg3" ) );
QCOMPARE( w->value().value< QgsProcessingModelChildParameterSource>().outputName(), QStringLiteral( "OUTPUT" ) );
// model output
delete w;
w = new QgsProcessingModelerParameterWidget( &model, "alg1", model.parameterDefinition( "test_dest" ), context );
QCOMPARE( w->parameterDefinition()->name(), QStringLiteral( "test_dest" ) );
// should default to being a model output for destination parameters, but with no value
QVERIFY( w->isModelOutput() );
QCOMPARE( w->modelOutputName(), QString() );
// set it to something else
w->setWidgetValue( QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg3" ), QStringLiteral( "OUTPUT" ) ) );
QVERIFY( !w->isModelOutput() );
// and back
w->setToModelOutput( QStringLiteral( "out" ) );
QVERIFY( w->isModelOutput() );
QCOMPARE( w->modelOutputName(), QStringLiteral( "out" ) );
w->setWidgetValue( QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg3" ), QStringLiteral( "OUTPUT" ) ) );
w->setToModelOutput( QString() );
QVERIFY( w->isModelOutput() );
QCOMPARE( w->modelOutputName(), QString() );
// multi-source input
delete w;
const QgsProcessingAlgorithm *packageAlg = QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:package" ) );
@ -7465,6 +7490,381 @@ void TestProcessingGui::testFeatureSourceOptionsWidget()
QCOMPARE( spy.count(), 5 );
}
void TestProcessingGui::testVectorOutWrapper()
{
auto testWrapper = [ = ]( QgsProcessingGui::WidgetType type )
{
// non optional
QgsProcessingParameterVectorDestination param( QStringLiteral( "vector" ), QStringLiteral( "vector" ) );
QgsProcessingVectorDestinationWidgetWrapper wrapper( &param, type );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingVectorDestinationWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( QStringLiteral( "/bb.shp" ), context );
switch ( type )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
case QgsProcessingGui::Modeler:
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
wrapper.setWidgetValue( QStringLiteral( "/aa.shp" ), context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/aa.shp" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/aa.shp" ) );
break;
}
// check signal
static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->setValue( QStringLiteral( "/cc.shp" ) );
QCOMPARE( spy.count(), 3 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/cc.shp" ) );
delete w;
// optional
QgsProcessingParameterVectorDestination param2( QStringLiteral( "vector" ), QStringLiteral( "vector" ), QgsProcessing::TypeVector, QVariant(), true );
QgsProcessingVectorDestinationWidgetWrapper wrapper3( &param2, type );
w = wrapper3.createWrappedWidget( context );
QSignalSpy spy3( &wrapper3, &QgsProcessingVectorDestinationWidgetWrapper::widgetValueHasChanged );
wrapper3.setWidgetValue( QStringLiteral( "/bb.shp" ), context );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( wrapper3.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper3.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
wrapper3.setWidgetValue( QVariant(), context );
QCOMPARE( spy3.count(), 2 );
QVERIFY( !wrapper3.widgetValue().isValid() );
delete w;
QLabel *l = wrapper.createWrappedLabel();
if ( wrapper.type() != QgsProcessingGui::Batch )
{
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "vector" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
}
else
{
QVERIFY( !l );
}
};
// standard wrapper
testWrapper( QgsProcessingGui::Standard );
// batch wrapper
// testWrapper( QgsProcessingGui::Batch );
// modeler wrapper
testWrapper( QgsProcessingGui::Modeler );
}
void TestProcessingGui::testSinkWrapper()
{
auto testWrapper = [ = ]( QgsProcessingGui::WidgetType type )
{
// non optional
QgsProcessingParameterFeatureSink param( QStringLiteral( "sink" ), QStringLiteral( "sink" ) );
QgsProcessingFeatureSinkWidgetWrapper wrapper( &param, type );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingFeatureSinkWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( QStringLiteral( "/bb.shp" ), context );
switch ( type )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
case QgsProcessingGui::Modeler:
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
wrapper.setWidgetValue( QStringLiteral( "/aa.shp" ), context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/aa.shp" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/aa.shp" ) );
break;
}
// check signal
static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->setValue( QStringLiteral( "/cc.shp" ) );
QCOMPARE( spy.count(), 3 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/cc.shp" ) );
delete w;
// optional
QgsProcessingParameterFeatureSink param2( QStringLiteral( "sink" ), QStringLiteral( "sink" ), QgsProcessing::TypeVector, QVariant(), true );
QgsProcessingFeatureSinkWidgetWrapper wrapper3( &param2, type );
w = wrapper3.createWrappedWidget( context );
QSignalSpy spy3( &wrapper3, &QgsProcessingFeatureSinkWidgetWrapper::widgetValueHasChanged );
wrapper3.setWidgetValue( QStringLiteral( "/bb.shp" ), context );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( wrapper3.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper3.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.shp" ) );
wrapper3.setWidgetValue( QVariant(), context );
QCOMPARE( spy3.count(), 2 );
QVERIFY( !wrapper3.widgetValue().isValid() );
delete w;
QLabel *l = wrapper.createWrappedLabel();
if ( wrapper.type() != QgsProcessingGui::Batch )
{
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "sink" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
}
else
{
QVERIFY( !l );
}
};
// standard wrapper
testWrapper( QgsProcessingGui::Standard );
// batch wrapper
// testWrapper( QgsProcessingGui::Batch );
// modeler wrapper
testWrapper( QgsProcessingGui::Modeler );
}
void TestProcessingGui::testRasterOutWrapper()
{
auto testWrapper = [ = ]( QgsProcessingGui::WidgetType type )
{
// non optional
QgsProcessingParameterRasterDestination param( QStringLiteral( "raster" ), QStringLiteral( "raster" ) );
QgsProcessingRasterDestinationWidgetWrapper wrapper( &param, type );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingRasterDestinationWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( QStringLiteral( "/bb.tif" ), context );
switch ( type )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
case QgsProcessingGui::Modeler:
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.tif" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.tif" ) );
wrapper.setWidgetValue( QStringLiteral( "/aa.tif" ), context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/aa.tif" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/aa.tif" ) );
break;
}
// check signal
static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->setValue( QStringLiteral( "/cc.tif" ) );
QCOMPARE( spy.count(), 3 );
QCOMPARE( wrapper.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/cc.tif" ) );
delete w;
// optional
QgsProcessingParameterRasterDestination param2( QStringLiteral( "raster" ), QStringLiteral( "raster" ), QVariant(), true );
QgsProcessingRasterDestinationWidgetWrapper wrapper3( &param2, type );
w = wrapper3.createWrappedWidget( context );
QSignalSpy spy3( &wrapper3, &QgsProcessingRasterDestinationWidgetWrapper::widgetValueHasChanged );
wrapper3.setWidgetValue( QStringLiteral( "/bb.tif" ), context );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( wrapper3.widgetValue().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.tif" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper3.wrappedWidget() )->value().value< QgsProcessingOutputLayerDefinition >().sink.staticValue().toString(), QStringLiteral( "/bb.tif" ) );
wrapper3.setWidgetValue( QVariant(), context );
QCOMPARE( spy3.count(), 2 );
QVERIFY( !wrapper3.widgetValue().isValid() );
delete w;
QLabel *l = wrapper.createWrappedLabel();
if ( wrapper.type() != QgsProcessingGui::Batch )
{
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "raster" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
}
else
{
QVERIFY( !l );
}
};
// standard wrapper
testWrapper( QgsProcessingGui::Standard );
// batch wrapper
// testWrapper( QgsProcessingGui::Batch );
// modeler wrapper
testWrapper( QgsProcessingGui::Modeler );
}
void TestProcessingGui::testFileOutWrapper()
{
auto testWrapper = [ = ]( QgsProcessingGui::WidgetType type )
{
// non optional
QgsProcessingParameterFileDestination param( QStringLiteral( "file" ), QStringLiteral( "file" ) );
QgsProcessingFileDestinationWidgetWrapper wrapper( &param, type );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingFileDestinationWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( QStringLiteral( "/bb.tif" ), context );
switch ( type )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
case QgsProcessingGui::Modeler:
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "/bb.tif" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().toString(), QStringLiteral( "/bb.tif" ) );
wrapper.setWidgetValue( QStringLiteral( "/aa.tif" ), context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "/aa.tif" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().toString(), QStringLiteral( "/aa.tif" ) );
break;
}
// check signal
static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->setValue( QStringLiteral( "/cc.tif" ) );
QCOMPARE( spy.count(), 3 );
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "/cc.tif" ) );
delete w;
// optional
QgsProcessingParameterFileDestination param2( QStringLiteral( "file" ), QStringLiteral( "file" ), QString(), QVariant(), true );
QgsProcessingFileDestinationWidgetWrapper wrapper3( &param2, type );
w = wrapper3.createWrappedWidget( context );
QSignalSpy spy3( &wrapper3, &QgsProcessingFileDestinationWidgetWrapper::widgetValueHasChanged );
wrapper3.setWidgetValue( QStringLiteral( "/bb.tif" ), context );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( wrapper3.widgetValue().toString(), QStringLiteral( "/bb.tif" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper3.wrappedWidget() )->value().toString(), QStringLiteral( "/bb.tif" ) );
wrapper3.setWidgetValue( QVariant(), context );
QCOMPARE( spy3.count(), 2 );
QVERIFY( !wrapper3.widgetValue().isValid() );
delete w;
QLabel *l = wrapper.createWrappedLabel();
if ( wrapper.type() != QgsProcessingGui::Batch )
{
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "file" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
}
else
{
QVERIFY( !l );
}
};
// standard wrapper
testWrapper( QgsProcessingGui::Standard );
// batch wrapper
// testWrapper( QgsProcessingGui::Batch );
// modeler wrapper
testWrapper( QgsProcessingGui::Modeler );
}
void TestProcessingGui::testFolderOutWrapper()
{
auto testWrapper = [ = ]( QgsProcessingGui::WidgetType type )
{
// non optional
QgsProcessingParameterFolderDestination param( QStringLiteral( "folder" ), QStringLiteral( "folder" ) );
QgsProcessingFolderDestinationWidgetWrapper wrapper( &param, type );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingFolderDestinationWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( QStringLiteral( "/bb" ), context );
switch ( type )
{
case QgsProcessingGui::Standard:
case QgsProcessingGui::Batch:
case QgsProcessingGui::Modeler:
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "/bb" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().toString(), QStringLiteral( "/bb" ) );
wrapper.setWidgetValue( QStringLiteral( "/aa" ), context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "/aa" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->value().toString(), QStringLiteral( "/aa" ) );
break;
}
// check signal
static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper.wrappedWidget() )->setValue( QStringLiteral( "/cc" ) );
QCOMPARE( spy.count(), 3 );
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "/cc" ) );
delete w;
// optional
QgsProcessingParameterFolderDestination param2( QStringLiteral( "folder" ), QStringLiteral( "folder" ), QVariant(), true );
QgsProcessingFolderDestinationWidgetWrapper wrapper3( &param2, type );
w = wrapper3.createWrappedWidget( context );
QSignalSpy spy3( &wrapper3, &QgsProcessingFolderDestinationWidgetWrapper::widgetValueHasChanged );
wrapper3.setWidgetValue( QStringLiteral( "/bb" ), context );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( wrapper3.widgetValue().toString(), QStringLiteral( "/bb" ) );
QCOMPARE( static_cast< QgsProcessingLayerOutputDestinationWidget * >( wrapper3.wrappedWidget() )->value().toString(), QStringLiteral( "/bb" ) );
wrapper3.setWidgetValue( QVariant(), context );
QCOMPARE( spy3.count(), 2 );
QVERIFY( !wrapper3.widgetValue().isValid() );
delete w;
QLabel *l = wrapper.createWrappedLabel();
if ( wrapper.type() != QgsProcessingGui::Batch )
{
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "folder" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
}
else
{
QVERIFY( !l );
}
};
// standard wrapper
testWrapper( QgsProcessingGui::Standard );
// batch wrapper
// testWrapper( QgsProcessingGui::Batch );
// modeler wrapper
testWrapper( QgsProcessingGui::Modeler );
}
void TestProcessingGui::cleanupTempDir()
{
QDir tmpDir = QDir( mTempDir );