Skip to content

Commit 55e75ad

Browse files
rldhontvolaya
authored andcommitted
[Processing] Optional parameters for script and model
In processing core every parameters can be optional but in scripts there is no way to define a parameter as optional and in modeler only layers can be optional. For script, I propose a notation like output, if a parameter token starts with optional the parameter is optional. For model, I propose to add the required combobox to all parameters. This proposition does not change the value setter. This proposition can fix the issue http://hub.qgis.org/issues/5488.
1 parent f7a7a78 commit 55e75ad

File tree

2 files changed

+125
-121
lines changed

2 files changed

+125
-121
lines changed

python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py

+32-56
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ def setupUi(self):
9292
self.horizontalLayout3 = QHBoxLayout(self)
9393
self.horizontalLayout3.setSpacing(2)
9494
self.horizontalLayout3.setMargin(0)
95+
self.horizontalLayout4 = QHBoxLayout(self)
96+
self.horizontalLayout4.setSpacing(2)
97+
self.horizontalLayout4.setMargin(0)
9598

9699
if isinstance(self.param, Parameter):
97100
self.nameTextBox.setText(self.param.description)
@@ -103,11 +106,11 @@ def setupUi(self):
103106
self.state.setChecked(False)
104107
if self.param is not None:
105108
self.state.setChecked(True if self.param.value else False)
106-
self.horizontalLayout2.addWidget(self.state)
107-
self.verticalLayout.addLayout(self.horizontalLayout2)
109+
self.horizontalLayout3.addWidget(self.state)
110+
self.verticalLayout.addLayout(self.horizontalLayout3)
108111
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE_FIELD or \
109112
isinstance(self.param, ParameterTableField):
110-
self.horizontalLayout2.addWidget(QLabel(self.tr('Parent layer')))
113+
self.horizontalLayout3.addWidget(QLabel(self.tr('Parent layer')))
111114
self.parentCombo = QComboBox()
112115
idx = 0
113116
for param in self.alg.inputs.values():
@@ -117,57 +120,22 @@ def setupUi(self):
117120
if self.param.parent == param.param.name:
118121
self.parentCombo.setCurrentIndex(idx)
119122
idx += 1
120-
self.horizontalLayout2.addWidget(self.parentCombo)
121-
self.verticalLayout.addLayout(self.horizontalLayout2)
122-
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_RASTER or \
123-
isinstance(self.param, ParameterRaster):
124-
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
125-
self.yesNoCombo = QComboBox()
126-
self.yesNoCombo.addItem(self.tr('Yes'))
127-
self.yesNoCombo.addItem(self.tr('No'))
128-
if self.param is not None:
129-
self.yesNoCombo.setCurrentIndex(
130-
1 if self.param.optional else 0)
131-
self.horizontalLayout2.addWidget(self.yesNoCombo)
132-
self.verticalLayout.addLayout(self.horizontalLayout2)
133-
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE or \
134-
isinstance(self.param, ParameterTable):
135-
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
136-
self.yesNoCombo = QComboBox()
137-
self.yesNoCombo.addItem(self.tr('Yes'))
138-
self.yesNoCombo.addItem(self.tr('No'))
139-
if self.param is not None:
140-
self.yesNoCombo.setCurrentIndex(
141-
1 if self.param.optional else 0)
142-
self.horizontalLayout2.addWidget(self.yesNoCombo)
143-
self.verticalLayout.addLayout(self.horizontalLayout2)
123+
self.horizontalLayout3.addWidget(self.parentCombo)
124+
self.verticalLayout.addLayout(self.horizontalLayout3)
144125
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or \
145126
isinstance(self.param, ParameterVector):
146-
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
147-
self.yesNoCombo = QComboBox()
148-
self.yesNoCombo.addItem(self.tr('Yes'))
149-
self.yesNoCombo.addItem(self.tr('No'))
150-
self.horizontalLayout2.addWidget(self.yesNoCombo)
151127
self.horizontalLayout3.addWidget(QLabel(self.tr('Shape type')))
152128
self.shapetypeCombo = QComboBox()
153129
self.shapetypeCombo.addItem(self.tr('Any'))
154130
self.shapetypeCombo.addItem(self.tr('Point'))
155131
self.shapetypeCombo.addItem(self.tr('Line'))
156132
self.shapetypeCombo.addItem(self.tr('Polygon'))
157133
if self.param is not None:
158-
self.yesNoCombo.setCurrentIndex(
159-
1 if self.param.optional else 0)
160134
self.shapetypeCombo.setCurrentIndex(self.param.shapetype[0] + 1)
161135
self.horizontalLayout3.addWidget(self.shapetypeCombo)
162136
self.verticalLayout.addLayout(self.horizontalLayout3)
163-
self.verticalLayout.addLayout(self.horizontalLayout2)
164137
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or \
165138
isinstance(self.param, ParameterMultipleInput):
166-
self.horizontalLayout2.addWidget(QLabel(self.tr('Mandatory')))
167-
self.yesNoCombo = QComboBox()
168-
self.yesNoCombo.addItem(self.tr('Yes'))
169-
self.yesNoCombo.addItem(self.tr('No'))
170-
self.horizontalLayout2.addWidget(self.yesNoCombo)
171139
self.horizontalLayout3.addWidget(QLabel(self.tr('Data type')))
172140
self.datatypeCombo = QComboBox()
173141
self.datatypeCombo.addItem(self.tr('Vector (any)'))
@@ -177,53 +145,60 @@ def setupUi(self):
177145
self.datatypeCombo.addItem(self.tr('Raster'))
178146
self.datatypeCombo.addItem(self.tr('Table'))
179147
if self.param is not None:
180-
self.yesNoCombo.setCurrentIndex(
181-
1 if self.param.optional else 0)
182148
self.datatypeCombo.setCurrentIndex(self.param.datatype + 1)
183149
self.horizontalLayout3.addWidget(self.datatypeCombo)
184150
self.verticalLayout.addLayout(self.horizontalLayout3)
185-
self.verticalLayout.addLayout(self.horizontalLayout2)
186151
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or \
187152
isinstance(self.param, ParameterNumber):
188-
self.horizontalLayout2.addWidget(QLabel(self.tr('Min/Max values')))
153+
self.horizontalLayout3.addWidget(QLabel(self.tr('Min/Max values')))
189154
self.minTextBox = QLineEdit()
190155
self.maxTextBox = QLineEdit()
191156
if self.param is not None:
192157
self.minTextBox.setText(unicode(self.param.min))
193158
self.maxTextBox.setText(unicode(self.param.max))
194-
self.horizontalLayout2.addWidget(self.minTextBox)
195-
self.horizontalLayout2.addWidget(self.maxTextBox)
196-
self.verticalLayout.addLayout(self.horizontalLayout2)
197-
self.horizontalLayout3.addWidget(QLabel(self.tr('Default value')))
159+
self.horizontalLayout3.addWidget(self.minTextBox)
160+
self.horizontalLayout3.addWidget(self.maxTextBox)
161+
self.verticalLayout.addLayout(self.horizontalLayout3)
162+
self.horizontalLayout4.addWidget(QLabel(self.tr('Default value')))
198163
self.defaultTextBox = QLineEdit()
199164
self.defaultTextBox.setText(self.tr('0'))
200165
if self.param is not None:
201166
default = self.param.default
202167
if self.param.isInteger:
203168
default = int(math.floor(default))
204169
self.defaultTextBox.setText(unicode(default))
205-
self.horizontalLayout3.addWidget(self.defaultTextBox)
206-
self.verticalLayout.addLayout(self.horizontalLayout3)
170+
self.horizontalLayout4.addWidget(self.defaultTextBox)
171+
self.verticalLayout.addLayout(self.horizontalLayout4)
207172
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_STRING or \
208173
isinstance(self.param, ParameterString):
209-
self.horizontalLayout2.addWidget(QLabel(self.tr('Default value')))
174+
self.horizontalLayout3.addWidget(QLabel(self.tr('Default value')))
210175
self.defaultTextBox = QLineEdit()
211176
if self.param is not None:
212177
self.defaultTextBox.setText(self.param.default)
213-
self.horizontalLayout2.addWidget(self.defaultTextBox)
214-
self.verticalLayout.addLayout(self.horizontalLayout2)
178+
self.horizontalLayout3.addWidget(self.defaultTextBox)
179+
self.verticalLayout.addLayout(self.horizontalLayout3)
215180
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_FILE or \
216181
isinstance(self.param, ParameterFile):
217-
self.horizontalLayout2.addWidget(QLabel(self.tr('Type')))
182+
self.horizontalLayout3.addWidget(QLabel(self.tr('Type')))
218183
self.fileFolderCombo = QComboBox()
219184
self.fileFolderCombo.addItem(self.tr('File'))
220185
self.fileFolderCombo.addItem(self.tr('Folder'))
221186
if self.param is not None:
222187
self.fileFolderCombo.setCurrentIndex(
223188
1 if self.param.isFolder else 0)
224-
self.horizontalLayout2.addWidget(self.fileFolderCombo)
225-
self.verticalLayout.addLayout(self.horizontalLayout2)
189+
self.horizontalLayout3.addWidget(self.fileFolderCombo)
190+
self.verticalLayout.addLayout(self.horizontalLayout3)
226191

192+
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
193+
self.yesNoCombo = QComboBox()
194+
self.yesNoCombo.addItem(self.tr('Yes'))
195+
self.yesNoCombo.addItem(self.tr('No'))
196+
self.horizontalLayout2.addWidget(self.yesNoCombo)
197+
if self.param is not None:
198+
self.yesNoCombo.setCurrentIndex(
199+
1 if self.param.optional else 0)
200+
self.verticalLayout.addLayout(self.horizontalLayout2)
201+
227202
self.buttonBox = QDialogButtonBox(self)
228203
self.buttonBox.setOrientation(Qt.Horizontal)
229204
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
@@ -315,6 +290,7 @@ def okPressed(self):
315290
isinstance(self.param, ParameterFile):
316291
isFolder = self.fileFolderCombo.currentIndex() == 1
317292
self.param = ParameterFile(name, description, isFolder=isFolder)
293+
self.param.optional = self.yesNoCombo.currentIndex() == 1
318294
self.close()
319295

320296
def cancelPressed(self):

python/plugins/processing/script/ScriptAlgorithm.py

+93-65
Original file line numberDiff line numberDiff line change
@@ -145,98 +145,126 @@ def processParameterLine(self, line):
145145
if tokens[1].lower().strip() == 'name':
146146
self.name = self.i18n_name = tokens[0]
147147
return
148-
if tokens[1].lower().strip() == 'raster':
149-
param = ParameterRaster(tokens[0], desc, False)
150-
elif tokens[1].lower().strip() == 'vector':
151-
param = ParameterVector(tokens[0], desc,
148+
149+
if tokens[1].lower().strip().startswith('output'):
150+
outToken = tokens[1].strip()[len('output') + 1:]
151+
out = self.processOutputParameterToken(outToken)
152+
153+
elif tokens[1].lower().strip().startswith('optional'):
154+
optToken = tokens[1].strip()[len('optional') + 1:]
155+
param = self.processInputParameterToken(optToken, tokens[0])
156+
if param:
157+
param.optional = True
158+
159+
else:
160+
param = self.processInputParameterToken(tokens[1], tokens[0])
161+
162+
163+
if param is not None:
164+
self.addParameter(param)
165+
elif out is not None:
166+
out.name = tokens[0]
167+
out.description = desc
168+
self.addOutput(out)
169+
else:
170+
raise WrongScriptException(
171+
self.tr('Could not load script: %s.\n'
172+
'Problem with line "%s"', 'ScriptAlgorithm') % (self.descriptionFile or '', line))
173+
174+
def processInputParameterToken(self, token, name):
175+
param = None
176+
177+
descName = self.createDescriptiveName(name)
178+
179+
if token.lower().strip() == 'raster':
180+
param = ParameterRaster(name, descName, False)
181+
elif token.lower().strip() == 'vector':
182+
param = ParameterVector(name, descName,
152183
[ParameterVector.VECTOR_TYPE_ANY])
153-
elif tokens[1].lower().strip() == 'vector point':
154-
param = ParameterVector(tokens[0], desc,
184+
elif token.lower().strip() == 'vector point':
185+
param = ParameterVector(name, descName,
155186
[ParameterVector.VECTOR_TYPE_POINT])
156-
elif tokens[1].lower().strip() == 'vector line':
157-
param = ParameterVector(tokens[0], desc,
187+
elif token.lower().strip() == 'vector line':
188+
param = ParameterVector(name, descName,
158189
[ParameterVector.VECTOR_TYPE_LINE])
159-
elif tokens[1].lower().strip() == 'vector polygon':
160-
param = ParameterVector(tokens[0], desc,
190+
elif token.lower().strip() == 'vector polygon':
191+
param = ParameterVector(name, descName,
161192
[ParameterVector.VECTOR_TYPE_POLYGON])
162-
elif tokens[1].lower().strip() == 'table':
163-
param = ParameterTable(tokens[0], desc, False)
164-
elif tokens[1].lower().strip() == 'multiple raster':
165-
param = ParameterMultipleInput(tokens[0], desc,
193+
elif token.lower().strip() == 'table':
194+
param = ParameterTable(name, descName, False)
195+
elif token.lower().strip() == 'multiple raster':
196+
param = ParameterMultipleInput(name, descName,
166197
ParameterMultipleInput.TYPE_RASTER)
167198
param.optional = False
168-
elif tokens[1].lower().strip() == 'multiple vector':
169-
param = ParameterMultipleInput(tokens[0], desc,
199+
elif token.lower().strip() == 'multiple vector':
200+
param = ParameterMultipleInput(name, descName,
170201
ParameterMultipleInput.TYPE_VECTOR_ANY)
171202
param.optional = False
172-
elif tokens[1].lower().strip().startswith('selectionfromfile'):
173-
options = tokens[1].strip()[len('selectionfromfile '):].split(';')
174-
param = ParameterSelection(tokens[0], desc, options, isSource=True)
175-
elif tokens[1].lower().strip().startswith('selection'):
203+
elif token.lower().strip().startswith('selectionfromfile'):
204+
options = token.strip()[len('selectionfromfile '):].split(';')
205+
param = ParameterSelection(name, descName, options, isSource=True)
206+
elif token.lower().strip().startswith('selection'):
176207
options = tokens[1].strip()[len('selection '):].split(';')
177-
param = ParameterSelection(tokens[0], desc, options)
178-
elif tokens[1].lower().strip().startswith('boolean'):
179-
default = tokens[1].strip()[len('boolean') + 1:]
180-
param = ParameterBoolean(tokens[0], desc, default)
181-
elif tokens[1].lower().strip() == 'extent':
182-
param = ParameterExtent(tokens[0], desc)
183-
elif tokens[1].lower().strip() == 'file':
184-
param = ParameterFile(tokens[0], desc, False)
185-
elif tokens[1].lower().strip() == 'folder':
186-
param = ParameterFile(tokens[0], desc, True)
187-
elif tokens[1].lower().strip().startswith('number'):
188-
default = tokens[1].strip()[len('number') + 1:]
189-
param = ParameterNumber(tokens[0], desc, default=default)
190-
elif tokens[1].lower().strip().startswith('field'):
191-
field = tokens[1].strip()[len('field') + 1:]
208+
param = ParameterSelection(name, descName, options)
209+
elif token.lower().strip().startswith('boolean'):
210+
default = token.strip()[len('boolean') + 1:]
211+
param = ParameterBoolean(name, descName, default)
212+
elif token.lower().strip() == 'extent':
213+
param = ParameterExtent(name, descName)
214+
elif token.lower().strip() == 'file':
215+
param = ParameterFile(name, descName, False)
216+
elif token.lower().strip() == 'folder':
217+
param = ParameterFile(name, descName, True)
218+
elif token.lower().strip().startswith('number'):
219+
default = token.strip()[len('number') + 1:]
220+
param = ParameterNumber(name, descName, default=default)
221+
elif token.lower().strip().startswith('field'):
222+
field = token.strip()[len('field') + 1:]
192223
found = False
193224
for p in self.parameters:
194225
if p.name == field:
195226
found = True
196227
break
197228
if found:
198-
param = ParameterTableField(tokens[0], desc, field)
199-
elif tokens[1].lower().strip().startswith('string'):
200-
default = tokens[1].strip()[len('string') + 1:]
201-
param = ParameterString(tokens[0], desc, default)
202-
elif tokens[1].lower().strip().startswith('longstring'):
203-
default = tokens[1].strip()[len('longstring') + 1:]
204-
param = ParameterString(tokens[0], desc, default, multiline=True)
205-
elif tokens[1].lower().strip().startswith('crs'):
206-
default = tokens[1].strip()[len('crs') + 1:]
229+
param = ParameterTableField(name, descName, field)
230+
elif token.lower().strip().startswith('string'):
231+
default = token.strip()[len('string') + 1:]
232+
param = ParameterString(name, descName, default)
233+
elif token.lower().strip().startswith('longstring'):
234+
default = token.strip()[len('longstring') + 1:]
235+
param = ParameterString(name, descName, default, multiline=True)
236+
elif token.lower().strip().startswith('crs'):
237+
default = token.strip()[len('crs') + 1:]
207238
if not default:
208239
default = 'EPSG:4326'
209-
param = ParameterCrs(tokens[0], desc, default)
210-
elif tokens[1].lower().strip().startswith('output raster'):
240+
param = ParameterCrs(name, descName, default)
241+
242+
return param
243+
244+
def processOutputParameterToken(self, token):
245+
out = None
246+
247+
if token.lower().strip().startswith('raster'):
211248
out = OutputRaster()
212-
elif tokens[1].lower().strip().startswith('output vector'):
249+
elif token.lower().strip().startswith('vector'):
213250
out = OutputVector()
214-
elif tokens[1].lower().strip().startswith('output table'):
251+
elif token.lower().strip().startswith('table'):
215252
out = OutputTable()
216-
elif tokens[1].lower().strip().startswith('output html'):
253+
elif token.lower().strip().startswith('html'):
217254
out = OutputHTML()
218-
elif tokens[1].lower().strip().startswith('output file'):
255+
elif token.lower().strip().startswith('file'):
219256
out = OutputFile()
220-
subtokens = tokens[1].split(' ')
257+
subtokens = token.split(' ')
221258
if len(subtokens) > 2:
222259
out.ext = subtokens[2]
223-
elif tokens[1].lower().strip().startswith('output directory'):
260+
elif token.lower().strip().startswith('directory'):
224261
out = OutputDirectory()
225-
elif tokens[1].lower().strip().startswith('output number'):
262+
elif token.lower().strip().startswith('number'):
226263
out = OutputNumber()
227-
elif tokens[1].lower().strip().startswith('output string'):
264+
elif token.lower().strip().startswith('string'):
228265
out = OutputString()
229-
230-
if param is not None:
231-
self.addParameter(param)
232-
elif out is not None:
233-
out.name = tokens[0]
234-
out.description = desc
235-
self.addOutput(out)
236-
else:
237-
raise WrongScriptException(
238-
self.tr('Could not load script: %s.\n'
239-
'Problem with line "%s"', 'ScriptAlgorithm') % (self.descriptionFile or '', line))
266+
267+
return out
240268

241269
def processDescriptionParameterLine(self, line):
242270
try:

0 commit comments

Comments
 (0)