Skip to content
Permalink
Browse files

[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.
  • Loading branch information
nyalldawson committed Feb 22, 2019
1 parent e31fb92 commit c1aac3228c44f5add412be77a5221af34fd1a671
@@ -79,14 +79,17 @@
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 @@ def __init__(self, model=None):

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 @@ def __init__(self, model=None):
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 @@ def __init__(self, model=None):
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 @@ def _mimeDataInput(items):
else:
self.model = QgsProcessingModelAlgorithm()
self.model.setProvider(QgsApplication.processingRegistry().providerById('model'))
self.update_variables_gui()

self.fillInputsTree()

@@ -492,6 +513,18 @@ def editHelp(self):
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 @@ def loadModel(self, filename):
self.textName.setText(alg.name())
self.repaintModel()

self.update_variables_gui()

self.view.centerOn(0, 0)
self.hasChanged = False
else:
@@ -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 );
@@ -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 );

0 comments on commit c1aac32

Please sign in to comment.
You can’t perform that action at this time.