Navigation Menu

Skip to content

Commit

Permalink
[FEATURE][processing] Port output parameter wrappers to new c++ API f…
Browse files Browse the repository at this point in the history
…or 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
  • Loading branch information
nyalldawson committed Apr 1, 2020
1 parent 3d9bd71 commit d20a3bd
Show file tree
Hide file tree
Showing 11 changed files with 620 additions and 54 deletions.
Expand Up @@ -30,6 +30,7 @@ Source for the value of a parameter for a child algorithm within a model.
StaticValue,
Expression,
ExpressionText,
ModelOutput,
};

QgsProcessingModelChildParameterSource();
Expand Down
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
119 changes: 74 additions & 45 deletions python/plugins/processing/modeler/ModelerParametersDialog.py
Expand Up @@ -35,11 +35,6 @@
QgsProcessingModelOutput,
QgsProcessingModelChildAlgorithm,
QgsProcessingModelChildParameterSource,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterFileDestination,
QgsProcessingParameterFolderDestination,
QgsProcessingParameterVectorDestination,
QgsProcessingOutputDefinition)

from qgis.gui import (QgsGui,
Expand Down Expand Up @@ -175,11 +170,8 @@ def algorithm(self):
return self._alg

def setupUi(self):
self.checkBoxes = {}
self.showAdvanced = False
self.wrappers = {}
self.valueItems = {}
self.dependentItems = {}
self.algorithmItem = None

self.mainLayout = QVBoxLayout()
Expand Down Expand Up @@ -246,8 +238,6 @@ def setupUi(self):
else:
widget = wrapper.widget
if widget is not None:
self.valueItems[param.name()] = widget

if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
label = wrapper.createLabel()
else:
Expand All @@ -263,19 +253,29 @@ def setupUi(self):
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)
Expand Down Expand Up @@ -401,9 +401,34 @@ def setPreviousValues(self):
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()
Expand Down Expand Up @@ -457,21 +482,31 @@ def createAlgorithm(self):
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

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
for output in self._alg.destinationParameterDefinitions():
if not output.flags() & QgsProcessingParameterDefinition.FlagHidden:
wrapper = self.wrappers[output.name()]

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)

Expand Down Expand Up @@ -521,12 +556,6 @@ def switchToCommentTab(self):
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()
Expand Down
6 changes: 6 additions & 0 deletions src/core/processing/models/qgsprocessingmodelalgorithm.cpp
Expand Up @@ -136,6 +136,9 @@ QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const QgsP
expressionText = QgsExpression::replaceExpressionText( source.expressionText(), &expressionContext );
break;
}

case QgsProcessingModelChildParameterSource::ModelOutput:
break;
}
}

Expand Down Expand Up @@ -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 ) );
Expand Down Expand Up @@ -901,6 +905,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
case QgsProcessingModelChildParameterSource::Expression:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ModelOutput:
continue;

};
Expand Down Expand Up @@ -959,6 +964,7 @@ QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingMode
case QgsProcessingModelChildParameterSource::Expression:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ModelOutput:
continue;

};
Expand Down
Expand Up @@ -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;
}
Expand Down Expand Up @@ -120,6 +122,9 @@ QVariant QgsProcessingModelChildParameterSource::toVariant() const
case ExpressionText:
map.insert( QStringLiteral( "expression_text" ), mExpressionText );
break;

case ModelOutput:
break;
}
return map;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -179,6 +187,9 @@ QString QgsProcessingModelChildParameterSource::asPythonCode( const QgsProcessin

case ExpressionText:
return mExpressionText;

case ModelOutput:
return QString();
}
return QString();
}
Expand Down Expand Up @@ -222,6 +233,9 @@ QString QgsProcessingModelChildParameterSource::friendlyIdentifier( QgsProcessin

case ExpressionText:
return mExpressionText;

case ModelOutput:
return QString();
}
return QString();
}
Expand Down
Expand Up @@ -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
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/gui/processing/models/qgsmodelgraphicsscene.cpp
Expand Up @@ -395,6 +395,7 @@ QList<QgsModelGraphicsScene::LinkSource> QgsModelGraphicsScene::linkSourcesForPa

case QgsProcessingModelChildParameterSource::StaticValue:
case QgsProcessingModelChildParameterSource::ExpressionText:
case QgsProcessingModelChildParameterSource::ModelOutput:
break;
}
}
Expand Down

0 comments on commit d20a3bd

Please sign in to comment.