Skip to content

Commit ee7daa8

Browse files
committed
[FEATURE][processing] Add option to calculate parameter values by expression in batch dialog
This new option is available under the Autofill menu for a column. Selecting it allows users to create a new QGIS expression to use to update the value inside that column. Existing parameter values (including those from other columns) are available for use inside the expression via @variables. E.g. this allows setting output file names to complex expressions like '/home/me/stuff/buffer_' || left(@input, 30) || '_' || @distance || '.shp'
1 parent 185172a commit ee7daa8

File tree

1 file changed

+58
-2
lines changed

1 file changed

+58
-2
lines changed

python/plugins/processing/gui/BatchPanel.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,15 @@
6565
QgsProcessingParameterRasterDestination,
6666
QgsProcessingParameterVectorDestination,
6767
QgsProcessingParameterFeatureSink,
68-
QgsProcessingOutputLayerDefinition
68+
QgsProcessingOutputLayerDefinition,
69+
QgsExpressionContextUtils,
70+
QgsExpression
6971
)
7072
from qgis.gui import (
7173
QgsProcessingParameterWidgetContext,
7274
QgsProcessingContextGenerator,
73-
QgsFindFilesByPatternDialog
75+
QgsFindFilesByPatternDialog,
76+
QgsExpressionBuilderDialog
7477
)
7578
from qgis.utils import iface
7679

@@ -120,12 +123,20 @@ def createMenu(self):
120123
fill_down_action.setToolTip(self.tr('Copy the first value down to all other rows'))
121124
self.menu.addAction(fill_down_action)
122125

126+
calculate_by_expression = QAction(QCoreApplication.translate('BatchPanel', 'Calculate by Expression…'),
127+
self.menu)
128+
calculate_by_expression.setIcon(QgsApplication.getThemeIcon('/mActionCalculateField.svg'))
129+
calculate_by_expression.triggered.connect(self.calculateByExpression)
130+
calculate_by_expression.setToolTip(self.tr('Calculates parameter values by evaluating an expression'))
131+
self.menu.addAction(calculate_by_expression)
132+
123133
if isinstance(self.parameterDefinition, (QgsProcessingParameterFile,
124134
QgsProcessingParameterMapLayer,
125135
QgsProcessingParameterRasterLayer,
126136
QgsProcessingParameterMeshLayer,
127137
QgsProcessingParameterVectorLayer,
128138
QgsProcessingParameterFeatureSource)):
139+
self.menu.addSeparator()
129140
find_by_pattern_action = QAction(QCoreApplication.translate('BatchPanel', 'Add Files by Pattern…'),
130141
self.menu)
131142
find_by_pattern_action.triggered.connect(self.addFilesByPattern)
@@ -177,6 +188,51 @@ def addFilesByPattern(self):
177188
for row, file in enumerate(files):
178189
self.setRowValue(first_row + row, file, context)
179190

191+
def calculateByExpression(self):
192+
"""
193+
Calculates parameter values by evaluating expressions.
194+
"""
195+
context = dataobjects.createContext()
196+
expression_context = context.expressionContext()
197+
198+
# use the first row parameter values as a preview during expression creation
199+
params = self.panel.parametersForRow(0, warnOnInvalid=False)
200+
alg_scope = QgsExpressionContextUtils.processingAlgorithmScope(self.panel.alg, params, context)
201+
202+
# create explicit variables corresponding to every parameter
203+
for k, v in params.items():
204+
alg_scope.setVariable(k, v, True)
205+
206+
expression_context.appendScope(alg_scope)
207+
208+
# mark the parameter variables as highlighted for discoverability
209+
highlighted_vars = expression_context.highlightedVariables()
210+
highlighted_vars.extend(list(params.keys()))
211+
expression_context.setHighlightedVariables(highlighted_vars)
212+
213+
dlg = QgsExpressionBuilderDialog(layer=None, context=context.expressionContext())
214+
if not dlg.exec_():
215+
return
216+
217+
for row in range(self.panel.batchRowCount()):
218+
params = self.panel.parametersForRow(row, warnOnInvalid=False)
219+
220+
# remove previous algorithm scope -- we need to rebuild this completely, using the
221+
# other parameter values from the current row
222+
expression_context.popScope()
223+
alg_scope = QgsExpressionContextUtils.processingAlgorithmScope(self.panel.alg, params, context)
224+
225+
for k, v in params.items():
226+
alg_scope.setVariable(k, v, True)
227+
228+
expression_context.appendScope(alg_scope)
229+
230+
# rebuild a new expression every time -- we don't want the expression compiler to replace
231+
# variables with precompiled values
232+
exp = QgsExpression(dlg.expressionText())
233+
value = exp.evaluate(expression_context)
234+
self.setRowValue(row, value, context)
235+
180236

181237
class BatchPanel(BASE, WIDGET):
182238
PARAMETERS = "PARAMETERS"

0 commit comments

Comments
 (0)