[FEATURE][processing] Allow expression variables to be set for a model

This adds a new "Model Variables" dock panel to the model editor, allowing
users to create and set custom expression variables for use in the model.
These variables are available anywhere expressions are (correctly) evaluated
within the model, so can be used as input parameter values for child
algorithms, within data-defined dynamic parameters, etc.

The use case here is for models which use a constant value throughout
multiple steps within the model (e.g. @target_resolution: a target
raster resolution, @max_simplification: a simplification value for
input features coming from different sources, etc), allowing users
one single place to define and edit these constant values (instead
of hunting down and setting them in multiple places throughout the model).

These variables are stored within the model itself, and are not exposed
outside of the model designer dialog.
This commit is contained in:
Nyall Dawson 2019-02-19 16:27:50 +10:00
parent e31fb920e7
commit c1aac3228c
3 changed files with 41 additions and 4 deletions

View File

@ -79,14 +79,17 @@ from qgis.core import (Qgis,
QgsProcessingUtils,
QgsProcessingModelAlgorithm,
QgsProcessingModelParameter,
QgsProcessingParameterType
QgsProcessingParameterType,
QgsExpressionContextScope,
QgsExpressionContext
)
from qgis.gui import (QgsMessageBar,
QgsDockWidget,
QgsScrollArea,
QgsFilterLineEdit,
QgsProcessingToolboxTreeView,
QgsProcessingToolboxProxyModel)
QgsProcessingToolboxProxyModel,
QgsVariableEditorWidget)
from processing.gui.HelpEditionDialog import HelpEditionDialog
from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
@ -140,6 +143,8 @@ class ModelerDialog(BASE, WIDGET):
self.setupUi(self)
self._variables_scope = None
# LOTS of bug reports when we include the dock creation in the UI file
# see e.g. #16428, #19068
# So just roll it all by hand......!
@ -181,8 +186,7 @@ class ModelerDialog(BASE, WIDGET):
self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1)
self.verticalDockLayout_1.addWidget(self.scrollArea_1)
self.propertiesDock.setWidget(propertiesDockContents)
self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock)
self.propertiesDock.setWindowTitle(self.tr("Model properties"))
self.propertiesDock.setWindowTitle(self.tr("Model Properties"))
self.inputsDock = QgsDockWidget(self)
self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
@ -243,6 +247,22 @@ class ModelerDialog(BASE, WIDGET):
self.searchBox.setToolTip(self.tr("Enter algorithm name to filter list"))
self.searchBox.setShowSearchIcon(True)
self.variables_dock = QgsDockWidget(self)
self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
self.variables_dock.setObjectName("variablesDock")
self.variables_dock_contents = QWidget()
vl_v = QVBoxLayout(self.algorithmsDockContents)
vl_v.setContentsMargins(0, 0, 0, 0)
self.variables_editor = QgsVariableEditorWidget()
vl_v.addWidget(self.variables_editor)
self.variables_dock_contents.setLayout(vl_v)
self.variables_dock.setWidget(self.variables_dock_contents)
self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock)
self.variables_dock.setWindowTitle(self.tr("Variables"))
self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock)
self.tabifyDockWidget(self.propertiesDock, self.variables_dock)
self.variables_editor.scopeChanged.connect(self.variables_changed)
self.bar = QgsMessageBar()
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
self.centralWidget().layout().insertWidget(0, self.bar)
@ -455,6 +475,7 @@ class ModelerDialog(BASE, WIDGET):
else:
self.model = QgsProcessingModelAlgorithm()
self.model.setProvider(QgsApplication.processingRegistry().providerById('model'))
self.update_variables_gui()
self.fillInputsTree()
@ -492,6 +513,18 @@ class ModelerDialog(BASE, WIDGET):
self.model.setHelpContent(dlg.descriptions)
self.hasChanged = True
def update_variables_gui(self):
variables_scope = QgsExpressionContextScope(self.tr('Model Variables'))
for k, v in self.model.variables().items():
variables_scope.setVariable(k, v)
variables_context = QgsExpressionContext()
variables_context.appendScope(variables_scope)
self.variables_editor.setContext(variables_context)
self.variables_editor.setEditableScopeIndex(0)
def variables_changed(self):
self.model.setVariables(self.variables_editor.variablesInActiveScope())
def runModel(self):
if len(self.model.childAlgorithms()) == 0:
self.bar.pushMessage("", self.tr("Model doesn't contain any algorithm and/or parameter and can't be executed"), level=Qgis.Warning, duration=5)
@ -722,6 +755,8 @@ class ModelerDialog(BASE, WIDGET):
self.textName.setText(alg.name())
self.repaintModel()
self.update_variables_gui()
self.view.centerOn(0, 0)
self.hasChanged = False
else:

View File

@ -198,6 +198,7 @@ QgsExpressionContext QgsProcessingModelerParameterWidget::createExpressionContex
QStringList highlightedVariables = childScope->variableNames();
QStringList highlightedFunctions = childScope->functionNames();
highlightedVariables += algorithmScope->variableNames();
highlightedVariables += mModel->variables().keys();
highlightedFunctions += algorithmScope->functionNames();
c.setHighlightedVariables( highlightedVariables );
c.setHighlightedFunctions( highlightedFunctions );

View File

@ -326,6 +326,7 @@ QgsExpressionContext QgsProcessingGuiUtils::createExpressionContext( QgsProcessi
QStringList highlightedVariables = childScope->variableNames();
QStringList highlightedFunctions = childScope->functionNames();
highlightedVariables += algorithmScope->variableNames();
highlightedVariables += widgetContext.model()->variables().keys();
highlightedFunctions += algorithmScope->functionNames();
c.setHighlightedVariables( highlightedVariables );
c.setHighlightedFunctions( highlightedFunctions );