Skip to content

Commit

Permalink
[Processing] Optional parameters for script and model
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rldhont authored and volaya committed Nov 4, 2015
1 parent f7a7a78 commit 55e75ad
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 121 deletions.
Expand Up @@ -92,6 +92,9 @@ def setupUi(self):
self.horizontalLayout3 = QHBoxLayout(self)
self.horizontalLayout3.setSpacing(2)
self.horizontalLayout3.setMargin(0)
self.horizontalLayout4 = QHBoxLayout(self)
self.horizontalLayout4.setSpacing(2)
self.horizontalLayout4.setMargin(0)

if isinstance(self.param, Parameter):
self.nameTextBox.setText(self.param.description)
Expand All @@ -103,11 +106,11 @@ def setupUi(self):
self.state.setChecked(False)
if self.param is not None:
self.state.setChecked(True if self.param.value else False)
self.horizontalLayout2.addWidget(self.state)
self.verticalLayout.addLayout(self.horizontalLayout2)
self.horizontalLayout3.addWidget(self.state)
self.verticalLayout.addLayout(self.horizontalLayout3)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE_FIELD or \
isinstance(self.param, ParameterTableField):
self.horizontalLayout2.addWidget(QLabel(self.tr('Parent layer')))
self.horizontalLayout3.addWidget(QLabel(self.tr('Parent layer')))
self.parentCombo = QComboBox()
idx = 0
for param in self.alg.inputs.values():
Expand All @@ -117,57 +120,22 @@ def setupUi(self):
if self.param.parent == param.param.name:
self.parentCombo.setCurrentIndex(idx)
idx += 1
self.horizontalLayout2.addWidget(self.parentCombo)
self.verticalLayout.addLayout(self.horizontalLayout2)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_RASTER or \
isinstance(self.param, ParameterRaster):
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
self.yesNoCombo = QComboBox()
self.yesNoCombo.addItem(self.tr('Yes'))
self.yesNoCombo.addItem(self.tr('No'))
if self.param is not None:
self.yesNoCombo.setCurrentIndex(
1 if self.param.optional else 0)
self.horizontalLayout2.addWidget(self.yesNoCombo)
self.verticalLayout.addLayout(self.horizontalLayout2)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE or \
isinstance(self.param, ParameterTable):
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
self.yesNoCombo = QComboBox()
self.yesNoCombo.addItem(self.tr('Yes'))
self.yesNoCombo.addItem(self.tr('No'))
if self.param is not None:
self.yesNoCombo.setCurrentIndex(
1 if self.param.optional else 0)
self.horizontalLayout2.addWidget(self.yesNoCombo)
self.verticalLayout.addLayout(self.horizontalLayout2)
self.horizontalLayout3.addWidget(self.parentCombo)
self.verticalLayout.addLayout(self.horizontalLayout3)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or \
isinstance(self.param, ParameterVector):
self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
self.yesNoCombo = QComboBox()
self.yesNoCombo.addItem(self.tr('Yes'))
self.yesNoCombo.addItem(self.tr('No'))
self.horizontalLayout2.addWidget(self.yesNoCombo)
self.horizontalLayout3.addWidget(QLabel(self.tr('Shape type')))
self.shapetypeCombo = QComboBox()
self.shapetypeCombo.addItem(self.tr('Any'))
self.shapetypeCombo.addItem(self.tr('Point'))
self.shapetypeCombo.addItem(self.tr('Line'))
self.shapetypeCombo.addItem(self.tr('Polygon'))
if self.param is not None:
self.yesNoCombo.setCurrentIndex(
1 if self.param.optional else 0)
self.shapetypeCombo.setCurrentIndex(self.param.shapetype[0] + 1)
self.horizontalLayout3.addWidget(self.shapetypeCombo)
self.verticalLayout.addLayout(self.horizontalLayout3)
self.verticalLayout.addLayout(self.horizontalLayout2)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or \
isinstance(self.param, ParameterMultipleInput):
self.horizontalLayout2.addWidget(QLabel(self.tr('Mandatory')))
self.yesNoCombo = QComboBox()
self.yesNoCombo.addItem(self.tr('Yes'))
self.yesNoCombo.addItem(self.tr('No'))
self.horizontalLayout2.addWidget(self.yesNoCombo)
self.horizontalLayout3.addWidget(QLabel(self.tr('Data type')))
self.datatypeCombo = QComboBox()
self.datatypeCombo.addItem(self.tr('Vector (any)'))
Expand All @@ -177,53 +145,60 @@ def setupUi(self):
self.datatypeCombo.addItem(self.tr('Raster'))
self.datatypeCombo.addItem(self.tr('Table'))
if self.param is not None:
self.yesNoCombo.setCurrentIndex(
1 if self.param.optional else 0)
self.datatypeCombo.setCurrentIndex(self.param.datatype + 1)
self.horizontalLayout3.addWidget(self.datatypeCombo)
self.verticalLayout.addLayout(self.horizontalLayout3)
self.verticalLayout.addLayout(self.horizontalLayout2)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or \
isinstance(self.param, ParameterNumber):
self.horizontalLayout2.addWidget(QLabel(self.tr('Min/Max values')))
self.horizontalLayout3.addWidget(QLabel(self.tr('Min/Max values')))
self.minTextBox = QLineEdit()
self.maxTextBox = QLineEdit()
if self.param is not None:
self.minTextBox.setText(unicode(self.param.min))
self.maxTextBox.setText(unicode(self.param.max))
self.horizontalLayout2.addWidget(self.minTextBox)
self.horizontalLayout2.addWidget(self.maxTextBox)
self.verticalLayout.addLayout(self.horizontalLayout2)
self.horizontalLayout3.addWidget(QLabel(self.tr('Default value')))
self.horizontalLayout3.addWidget(self.minTextBox)
self.horizontalLayout3.addWidget(self.maxTextBox)
self.verticalLayout.addLayout(self.horizontalLayout3)
self.horizontalLayout4.addWidget(QLabel(self.tr('Default value')))
self.defaultTextBox = QLineEdit()
self.defaultTextBox.setText(self.tr('0'))
if self.param is not None:
default = self.param.default
if self.param.isInteger:
default = int(math.floor(default))
self.defaultTextBox.setText(unicode(default))
self.horizontalLayout3.addWidget(self.defaultTextBox)
self.verticalLayout.addLayout(self.horizontalLayout3)
self.horizontalLayout4.addWidget(self.defaultTextBox)
self.verticalLayout.addLayout(self.horizontalLayout4)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_STRING or \
isinstance(self.param, ParameterString):
self.horizontalLayout2.addWidget(QLabel(self.tr('Default value')))
self.horizontalLayout3.addWidget(QLabel(self.tr('Default value')))
self.defaultTextBox = QLineEdit()
if self.param is not None:
self.defaultTextBox.setText(self.param.default)
self.horizontalLayout2.addWidget(self.defaultTextBox)
self.verticalLayout.addLayout(self.horizontalLayout2)
self.horizontalLayout3.addWidget(self.defaultTextBox)
self.verticalLayout.addLayout(self.horizontalLayout3)
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_FILE or \
isinstance(self.param, ParameterFile):
self.horizontalLayout2.addWidget(QLabel(self.tr('Type')))
self.horizontalLayout3.addWidget(QLabel(self.tr('Type')))
self.fileFolderCombo = QComboBox()
self.fileFolderCombo.addItem(self.tr('File'))
self.fileFolderCombo.addItem(self.tr('Folder'))
if self.param is not None:
self.fileFolderCombo.setCurrentIndex(
1 if self.param.isFolder else 0)
self.horizontalLayout2.addWidget(self.fileFolderCombo)
self.verticalLayout.addLayout(self.horizontalLayout2)
self.horizontalLayout3.addWidget(self.fileFolderCombo)
self.verticalLayout.addLayout(self.horizontalLayout3)

self.horizontalLayout2.addWidget(QLabel(self.tr('Required')))
self.yesNoCombo = QComboBox()
self.yesNoCombo.addItem(self.tr('Yes'))
self.yesNoCombo.addItem(self.tr('No'))
self.horizontalLayout2.addWidget(self.yesNoCombo)
if self.param is not None:
self.yesNoCombo.setCurrentIndex(
1 if self.param.optional else 0)
self.verticalLayout.addLayout(self.horizontalLayout2)

self.buttonBox = QDialogButtonBox(self)
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
Expand Down Expand Up @@ -315,6 +290,7 @@ def okPressed(self):
isinstance(self.param, ParameterFile):
isFolder = self.fileFolderCombo.currentIndex() == 1
self.param = ParameterFile(name, description, isFolder=isFolder)
self.param.optional = self.yesNoCombo.currentIndex() == 1
self.close()

def cancelPressed(self):
Expand Down
158 changes: 93 additions & 65 deletions python/plugins/processing/script/ScriptAlgorithm.py
Expand Up @@ -145,98 +145,126 @@ def processParameterLine(self, line):
if tokens[1].lower().strip() == 'name':
self.name = self.i18n_name = tokens[0]
return
if tokens[1].lower().strip() == 'raster':
param = ParameterRaster(tokens[0], desc, False)
elif tokens[1].lower().strip() == 'vector':
param = ParameterVector(tokens[0], desc,

if tokens[1].lower().strip().startswith('output'):
outToken = tokens[1].strip()[len('output') + 1:]
out = self.processOutputParameterToken(outToken)

elif tokens[1].lower().strip().startswith('optional'):
optToken = tokens[1].strip()[len('optional') + 1:]
param = self.processInputParameterToken(optToken, tokens[0])
if param:
param.optional = True

else:
param = self.processInputParameterToken(tokens[1], tokens[0])


if param is not None:
self.addParameter(param)
elif out is not None:
out.name = tokens[0]
out.description = desc
self.addOutput(out)
else:
raise WrongScriptException(
self.tr('Could not load script: %s.\n'
'Problem with line "%s"', 'ScriptAlgorithm') % (self.descriptionFile or '', line))

def processInputParameterToken(self, token, name):
param = None

descName = self.createDescriptiveName(name)

if token.lower().strip() == 'raster':
param = ParameterRaster(name, descName, False)
elif token.lower().strip() == 'vector':
param = ParameterVector(name, descName,
[ParameterVector.VECTOR_TYPE_ANY])
elif tokens[1].lower().strip() == 'vector point':
param = ParameterVector(tokens[0], desc,
elif token.lower().strip() == 'vector point':
param = ParameterVector(name, descName,
[ParameterVector.VECTOR_TYPE_POINT])
elif tokens[1].lower().strip() == 'vector line':
param = ParameterVector(tokens[0], desc,
elif token.lower().strip() == 'vector line':
param = ParameterVector(name, descName,
[ParameterVector.VECTOR_TYPE_LINE])
elif tokens[1].lower().strip() == 'vector polygon':
param = ParameterVector(tokens[0], desc,
elif token.lower().strip() == 'vector polygon':
param = ParameterVector(name, descName,
[ParameterVector.VECTOR_TYPE_POLYGON])
elif tokens[1].lower().strip() == 'table':
param = ParameterTable(tokens[0], desc, False)
elif tokens[1].lower().strip() == 'multiple raster':
param = ParameterMultipleInput(tokens[0], desc,
elif token.lower().strip() == 'table':
param = ParameterTable(name, descName, False)
elif token.lower().strip() == 'multiple raster':
param = ParameterMultipleInput(name, descName,
ParameterMultipleInput.TYPE_RASTER)
param.optional = False
elif tokens[1].lower().strip() == 'multiple vector':
param = ParameterMultipleInput(tokens[0], desc,
elif token.lower().strip() == 'multiple vector':
param = ParameterMultipleInput(name, descName,
ParameterMultipleInput.TYPE_VECTOR_ANY)
param.optional = False
elif tokens[1].lower().strip().startswith('selectionfromfile'):
options = tokens[1].strip()[len('selectionfromfile '):].split(';')
param = ParameterSelection(tokens[0], desc, options, isSource=True)
elif tokens[1].lower().strip().startswith('selection'):
elif token.lower().strip().startswith('selectionfromfile'):
options = token.strip()[len('selectionfromfile '):].split(';')
param = ParameterSelection(name, descName, options, isSource=True)
elif token.lower().strip().startswith('selection'):
options = tokens[1].strip()[len('selection '):].split(';')
param = ParameterSelection(tokens[0], desc, options)
elif tokens[1].lower().strip().startswith('boolean'):
default = tokens[1].strip()[len('boolean') + 1:]
param = ParameterBoolean(tokens[0], desc, default)
elif tokens[1].lower().strip() == 'extent':
param = ParameterExtent(tokens[0], desc)
elif tokens[1].lower().strip() == 'file':
param = ParameterFile(tokens[0], desc, False)
elif tokens[1].lower().strip() == 'folder':
param = ParameterFile(tokens[0], desc, True)
elif tokens[1].lower().strip().startswith('number'):
default = tokens[1].strip()[len('number') + 1:]
param = ParameterNumber(tokens[0], desc, default=default)
elif tokens[1].lower().strip().startswith('field'):
field = tokens[1].strip()[len('field') + 1:]
param = ParameterSelection(name, descName, options)
elif token.lower().strip().startswith('boolean'):
default = token.strip()[len('boolean') + 1:]
param = ParameterBoolean(name, descName, default)
elif token.lower().strip() == 'extent':
param = ParameterExtent(name, descName)
elif token.lower().strip() == 'file':
param = ParameterFile(name, descName, False)
elif token.lower().strip() == 'folder':
param = ParameterFile(name, descName, True)
elif token.lower().strip().startswith('number'):
default = token.strip()[len('number') + 1:]
param = ParameterNumber(name, descName, default=default)
elif token.lower().strip().startswith('field'):
field = token.strip()[len('field') + 1:]
found = False
for p in self.parameters:
if p.name == field:
found = True
break
if found:
param = ParameterTableField(tokens[0], desc, field)
elif tokens[1].lower().strip().startswith('string'):
default = tokens[1].strip()[len('string') + 1:]
param = ParameterString(tokens[0], desc, default)
elif tokens[1].lower().strip().startswith('longstring'):
default = tokens[1].strip()[len('longstring') + 1:]
param = ParameterString(tokens[0], desc, default, multiline=True)
elif tokens[1].lower().strip().startswith('crs'):
default = tokens[1].strip()[len('crs') + 1:]
param = ParameterTableField(name, descName, field)
elif token.lower().strip().startswith('string'):
default = token.strip()[len('string') + 1:]
param = ParameterString(name, descName, default)
elif token.lower().strip().startswith('longstring'):
default = token.strip()[len('longstring') + 1:]
param = ParameterString(name, descName, default, multiline=True)
elif token.lower().strip().startswith('crs'):
default = token.strip()[len('crs') + 1:]
if not default:
default = 'EPSG:4326'
param = ParameterCrs(tokens[0], desc, default)
elif tokens[1].lower().strip().startswith('output raster'):
param = ParameterCrs(name, descName, default)

return param

def processOutputParameterToken(self, token):
out = None

if token.lower().strip().startswith('raster'):
out = OutputRaster()
elif tokens[1].lower().strip().startswith('output vector'):
elif token.lower().strip().startswith('vector'):
out = OutputVector()
elif tokens[1].lower().strip().startswith('output table'):
elif token.lower().strip().startswith('table'):
out = OutputTable()
elif tokens[1].lower().strip().startswith('output html'):
elif token.lower().strip().startswith('html'):
out = OutputHTML()
elif tokens[1].lower().strip().startswith('output file'):
elif token.lower().strip().startswith('file'):
out = OutputFile()
subtokens = tokens[1].split(' ')
subtokens = token.split(' ')
if len(subtokens) > 2:
out.ext = subtokens[2]
elif tokens[1].lower().strip().startswith('output directory'):
elif token.lower().strip().startswith('directory'):
out = OutputDirectory()
elif tokens[1].lower().strip().startswith('output number'):
elif token.lower().strip().startswith('number'):
out = OutputNumber()
elif tokens[1].lower().strip().startswith('output string'):
elif token.lower().strip().startswith('string'):
out = OutputString()

if param is not None:
self.addParameter(param)
elif out is not None:
out.name = tokens[0]
out.description = desc
self.addOutput(out)
else:
raise WrongScriptException(
self.tr('Could not load script: %s.\n'
'Problem with line "%s"', 'ScriptAlgorithm') % (self.descriptionFile or '', line))

return out

def processDescriptionParameterLine(self, line):
try:
Expand Down

0 comments on commit 55e75ad

Please sign in to comment.