QGIS/python/plugins/processing/modeler/ModelerParametersDialog.py

433 lines
19 KiB
Python
Raw Normal View History

2012-10-04 19:33:47 +02:00
# -*- coding: utf-8 -*-
"""
***************************************************************************
ModelerParametersDialog.py
---------------------
Date : August 2012
Copyright : (C) 2012 by Victor Olaya
Email : volayaf at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
2012-10-04 19:33:47 +02:00
__author__ = 'Victor Olaya'
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'
import webbrowser
from qgis.PyQt.QtCore import (Qt,
QUrl,
QMetaObject,
QByteArray)
from qgis.PyQt.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QLineEdit,
QFrame, QPushButton, QSizePolicy, QVBoxLayout,
QHBoxLayout, QWidget)
from qgis.core import (Qgis,
QgsProject,
QgsProcessingParameterDefinition,
2017-06-13 15:51:40 +10:00
QgsProcessingParameterPoint,
2017-06-13 16:05:59 +10:00
QgsProcessingParameterExtent,
2017-06-20 20:40:19 +10:00
QgsProcessingModelAlgorithm,
QgsProcessingModelOutput,
QgsProcessingModelChildAlgorithm,
QgsProcessingModelChildParameterSource,
2017-06-20 20:40:19 +10:00
QgsProcessingParameterFeatureSink,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterFileDestination,
QgsProcessingParameterFolderDestination,
QgsProcessingParameterVectorDestination,
QgsProcessingOutputDefinition,
QgsSettings)
from qgis.gui import (QgsGui,
QgsMessageBar,
2017-07-08 15:58:56 +10:00
QgsScrollArea,
QgsFilterLineEdit,
QgsHelp,
QgsProcessingContextGenerator,
2018-09-20 08:26:42 +10:00
QgsProcessingModelerParameterWidget,
QgsProcessingParameterWidgetContext)
from qgis.utils import iface
2017-05-16 16:36:00 +10:00
from processing.gui.wrappers import WidgetWrapperFactory
from processing.gui.wrappers import InvalidParameterValue
from processing.gui.MultipleInputPanel import MultipleInputPanel
from processing.tools.dataobjects import createContext
from processing.gui.wrappers import WidgetWrapper
class ModelerParametersDialog(QDialog):
def __init__(self, alg, model, algName=None, configuration=None):
QDialog.__init__(self)
2012-09-15 18:25:25 +03:00
self.setModal(True)
self._alg = alg # The algorithm to define in this dialog. It is an instance of QgsProcessingAlgorithm
self.model = model # The model this algorithm is going to be added to. It is an instance of QgsProcessingModelAlgorithm
self.childId = algName # The name of the algorithm in the model, in case we are editing it and not defining it for the first time
self.configuration = configuration
self.context = createContext()
self.widget_labels = {}
class ContextGenerator(QgsProcessingContextGenerator):
def __init__(self, context):
super().__init__()
self.processing_context = context
def processingContext(self):
return self.processing_context
self.context_generator = ContextGenerator(self.context)
2012-09-15 18:25:25 +03:00
self.setupUi()
self.params = None
settings = QgsSettings()
self.restoreGeometry(settings.value("/Processing/modelParametersDialogGeometry", QByteArray()))
def closeEvent(self, event):
settings = QgsSettings()
settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry())
super(ModelerParametersDialog, self).closeEvent(event)
2012-09-15 18:25:25 +03:00
def setupUi(self):
self.checkBoxes = {}
self.showAdvanced = False
self.wrappers = {}
2012-09-15 18:25:25 +03:00
self.valueItems = {}
self.dependentItems = {}
self.algorithmItem = None
2012-09-15 18:25:25 +03:00
self.resize(650, 450)
self.buttonBox = QDialogButtonBox()
self.buttonBox.setOrientation(Qt.Horizontal)
2017-06-13 15:51:40 +10:00
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help)
self.setSizePolicy(QSizePolicy.Expanding,
QSizePolicy.Expanding)
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setSpacing(5)
self.verticalLayout.setMargin(20)
2014-07-02 07:46:03 +02:00
self.bar = QgsMessageBar()
self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
self.verticalLayout.addWidget(self.bar)
hLayout = QHBoxLayout()
hLayout.setSpacing(5)
hLayout.setMargin(0)
descriptionLabel = QLabel(self.tr("Description"))
self.descriptionBox = QLineEdit()
self.descriptionBox.setText(self._alg.displayName())
hLayout.addWidget(descriptionLabel)
hLayout.addWidget(self.descriptionBox)
self.verticalLayout.addLayout(hLayout)
line = QFrame()
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
self.verticalLayout.addWidget(line)
2014-07-02 07:46:03 +02:00
2018-09-20 08:26:42 +10:00
widget_context = QgsProcessingParameterWidgetContext()
widget_context.setProject(QgsProject.instance())
2018-09-21 14:55:03 +10:00
if iface is not None:
widget_context.setMapCanvas(iface.mapCanvas())
widget_context.setModel(self.model)
widget_context.setModelChildAlgorithmId(self.childId)
2018-09-20 08:26:42 +10:00
self.algorithmItem = QgsGui.instance().processingGuiRegistry().algorithmConfigurationWidget(self._alg)
if self.algorithmItem:
self.algorithmItem.setWidgetContext(widget_context)
self.algorithmItem.registerProcessingContextGenerator(self.context_generator)
if self.configuration:
self.algorithmItem.setConfiguration(self.configuration)
self.verticalLayout.addWidget(self.algorithmItem)
2017-06-13 15:51:40 +10:00
for param in self._alg.parameterDefinitions():
2017-05-16 16:36:00 +10:00
if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced:
self.advancedButton = QPushButton()
self.advancedButton.setText(self.tr('Show advanced parameters'))
self.advancedButton.clicked.connect(
self.showAdvancedParametersClicked)
advancedButtonHLayout = QHBoxLayout()
advancedButtonHLayout.addWidget(self.advancedButton)
advancedButtonHLayout.addStretch()
self.verticalLayout.addLayout(advancedButtonHLayout)
break
2017-05-16 16:36:00 +10:00
for param in self._alg.parameterDefinitions():
2017-06-13 15:51:40 +10:00
if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
2017-05-16 16:36:00 +10:00
wrapper = WidgetWrapperFactory.create_wrapper(param, self)
self.wrappers[param.name()] = wrapper
wrapper.setWidgetContext(widget_context)
wrapper.registerProcessingContextGenerator(self.context_generator)
if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
widget = wrapper
else:
widget = wrapper.widget
if widget is not None:
2017-05-16 16:36:00 +10:00
self.valueItems[param.name()] = widget
if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
label = wrapper.createLabel()
else:
tooltip = param.description()
widget.setToolTip(tooltip)
label = wrapper.label
self.widget_labels[param.name()] = label
2017-05-16 16:36:00 +10:00
if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced:
label.setVisible(self.showAdvanced)
widget.setVisible(self.showAdvanced)
self.verticalLayout.addWidget(label)
self.verticalLayout.addWidget(widget)
2017-06-20 20:40:19 +10:00
for dest in self._alg.destinationParameterDefinitions():
if dest.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination,
QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)):
2017-06-20 20:40:19 +10:00
label = QLabel(dest.description())
2017-07-08 15:58:56 +10:00
item = QgsFilterLineEdit()
2017-06-20 20:40:19 +10:00
if hasattr(item, 'setPlaceholderText'):
2018-04-05 08:25:20 +02:00
item.setPlaceholderText(self.tr('[Enter name if this is a final result]'))
2017-06-20 20:40:19 +10:00
self.verticalLayout.addWidget(label)
self.verticalLayout.addWidget(item)
self.valueItems[dest.name()] = item
2013-02-07 01:09:39 +01:00
label = QLabel(' ')
2013-02-03 10:26:43 +01:00
self.verticalLayout.addWidget(label)
label = QLabel(self.tr('Parent algorithms'))
2013-02-07 01:09:39 +01:00
self.dependenciesPanel = self.getDependenciesPanel()
2013-02-03 10:26:43 +01:00
self.verticalLayout.addWidget(label)
self.verticalLayout.addWidget(self.dependenciesPanel)
self.verticalLayout.addStretch(1000)
2012-09-15 18:25:25 +03:00
self.setPreviousValues()
self.setWindowTitle(self._alg.displayName())
self.verticalLayout2 = QVBoxLayout()
self.verticalLayout2.setSpacing(2)
self.verticalLayout2.setMargin(0)
self.paramPanel = QWidget()
self.paramPanel.setLayout(self.verticalLayout)
self.scrollArea = QgsScrollArea()
self.scrollArea.setWidget(self.paramPanel)
self.scrollArea.setWidgetResizable(True)
2014-04-19 22:04:24 +02:00
self.verticalLayout2.addWidget(self.scrollArea)
self.verticalLayout2.addWidget(self.buttonBox)
self.setLayout(self.verticalLayout2)
self.buttonBox.accepted.connect(self.okPressed)
self.buttonBox.rejected.connect(self.cancelPressed)
self.buttonBox.helpRequested.connect(self.openHelp)
QMetaObject.connectSlotsByName(self)
2012-09-15 18:25:25 +03:00
2017-01-23 09:24:31 +10:00
def getAvailableDependencies(self): # spellok
2017-07-03 19:42:29 +10:00
if self.childId is None:
2013-02-03 10:26:43 +01:00
dependent = []
else:
2017-07-03 19:42:29 +10:00
dependent = list(self.model.dependentChildAlgorithms(self.childId))
dependent.append(self.childId)
2013-02-07 01:09:39 +01:00
opts = []
2017-06-13 16:05:59 +10:00
for alg in list(self.model.childAlgorithms().values()):
if alg.childId() not in dependent:
opts.append(alg)
2013-02-03 10:26:43 +01:00
return opts
def getDependenciesPanel(self):
2017-06-20 19:23:21 +10:00
return MultipleInputPanel([alg.description() for alg in self.getAvailableDependencies()]) # spellok
2013-02-07 01:09:39 +01:00
def showAdvancedParametersClicked(self):
self.showAdvanced = not self.showAdvanced
if self.showAdvanced:
self.advancedButton.setText(self.tr('Hide advanced parameters'))
else:
self.advancedButton.setText(self.tr('Show advanced parameters'))
2017-06-13 15:51:40 +10:00
for param in self._alg.parameterDefinitions():
2017-05-16 16:36:00 +10:00
if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced:
wrapper = self.wrappers[param.name()]
if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
wrapper.setVisible(self.showAdvanced)
else:
wrapper.widget.setVisible(self.showAdvanced)
self.widget_labels[param.name()].setVisible(self.showAdvanced)
2012-12-10 00:12:07 +01:00
2017-07-03 19:42:29 +10:00
def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]):
# upgrade paramType to list
2017-06-13 15:51:40 +10:00
if paramType is None:
paramType = []
elif not isinstance(paramType, (tuple, list)):
paramType = [paramType]
2017-06-13 15:51:40 +10:00
if outTypes is None:
outTypes = []
elif not isinstance(outTypes, (tuple, list)):
2017-06-13 15:51:40 +10:00
outTypes = [outTypes]
2017-08-23 00:38:28 +10:00
return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if
issubclass(p, QgsProcessingParameterDefinition)],
[o.typeName() for o in outTypes if
issubclass(o, QgsProcessingOutputDefinition)], dataTypes)
2014-07-02 07:46:03 +02:00
def resolveValueDescription(self, value):
if isinstance(value, QgsProcessingModelChildParameterSource):
if value.source() == QgsProcessingModelChildParameterSource.StaticValue:
2017-06-13 16:05:59 +10:00
return value.staticValue()
elif value.source() == QgsProcessingModelChildParameterSource.ModelParameter:
2017-06-13 16:05:59 +10:00
return self.model.parameterDefinition(value.parameterName()).description()
elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput:
2017-06-13 16:05:59 +10:00
alg = self.model.childAlgorithm(value.outputChildId())
2017-06-15 20:28:39 +10:00
return self.tr("'{0}' from algorithm '{1}'").format(
alg.algorithm().outputDefinition(value.outputName()).description(), alg.description())
2017-06-13 16:05:59 +10:00
return value
2012-09-15 18:25:25 +03:00
2014-07-02 07:46:03 +02:00
def setPreviousValues(self):
2017-07-03 19:42:29 +10:00
if self.childId is not None:
alg = self.model.childAlgorithm(self.childId)
2017-06-13 16:05:59 +10:00
self.descriptionBox.setText(alg.description())
for param in alg.algorithm().parameterDefinitions():
2017-06-13 15:51:40 +10:00
if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
value = None
2017-06-13 16:05:59 +10:00
if param.name() in alg.parameterSources():
value = alg.parameterSources()[param.name()]
if isinstance(value, list) and len(value) == 1:
value = value[0]
elif isinstance(value, list) and len(value) == 0:
value = None
2017-06-20 19:23:21 +10:00
wrapper = self.wrappers[param.name()]
if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
if value is None:
value = QgsProcessingModelChildParameterSource.fromStaticValue(param.defaultValue())
wrapper.setWidgetValue(value)
else:
if value is None:
value = param.defaultValue()
if isinstance(value,
QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue:
value = value.staticValue()
wrapper.setValue(value)
2017-06-20 19:23:21 +10:00
for name, out in alg.modelOutputs().items():
if out.childOutputName() in self.valueItems:
self.valueItems[out.childOutputName()].setText(out.name())
2013-02-07 01:09:39 +01:00
2013-02-03 10:26:43 +01:00
selected = []
2017-01-23 09:24:31 +10:00
dependencies = self.getAvailableDependencies() # spellok
for idx, dependency in enumerate(dependencies):
2017-06-13 16:05:59 +10:00
if dependency.childId() in alg.dependencies():
selected.append(idx)
2013-02-07 01:09:39 +01:00
2013-02-03 10:26:43 +01:00
self.dependenciesPanel.setSelectedItems(selected)
2012-09-15 18:25:25 +03:00
def createAlgorithm(self):
alg = QgsProcessingModelChildAlgorithm(self._alg.id())
2017-07-03 19:42:29 +10:00
if not self.childId:
2017-06-20 20:40:19 +10:00
alg.generateChildId(self.model)
else:
2017-07-03 19:42:29 +10:00
alg.setChildId(self.childId)
2017-06-13 16:05:59 +10:00
alg.setDescription(self.descriptionBox.text())
if self.algorithmItem:
alg.setConfiguration(self.algorithmItem.configuration())
self._alg = alg.algorithm().create(self.algorithmItem.configuration())
2017-06-13 15:51:40 +10:00
for param in self._alg.parameterDefinitions():
if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden:
2012-09-15 18:25:25 +03:00
continue
try:
wrapper = self.wrappers[param.name()]
if issubclass(wrapper.__class__, WidgetWrapper):
val = wrapper.value()
elif issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
val = wrapper.value()
else:
val = wrapper.parameterValue()
except InvalidParameterValue:
2017-08-23 00:38:28 +10:00
self.bar.pushMessage(self.tr("Error"),
self.tr("Wrong or missing value for parameter '{}'").format(param.description()),
level=Qgis.Warning)
return None
if isinstance(val, QgsProcessingModelChildParameterSource):
val = [val]
2017-08-23 00:38:28 +10:00
elif not (isinstance(val, list) and all(
[isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])):
val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)]
for subval in val:
if (isinstance(subval, QgsProcessingModelChildParameterSource) and
subval.source() == QgsProcessingModelChildParameterSource.StaticValue and
2017-08-23 00:38:28 +10:00
not param.checkValueIsAcceptable(subval.staticValue())) \
or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional):
2017-08-23 00:38:28 +10:00
self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(
param.description()),
level=Qgis.Warning)
return None
alg.addParameterSources(param.name(), val)
2017-06-13 15:51:40 +10:00
2017-06-20 20:40:19 +10:00
outputs = {}
for dest in self._alg.destinationParameterDefinitions():
if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden:
2018-04-05 22:32:40 +02:00
name = self.valueItems[dest.name()].text()
2018-04-05 08:25:20 +02:00
if name.strip() != '':
output = QgsProcessingModelOutput(name, name)
2017-06-20 20:40:19 +10:00
output.setChildId(alg.childId())
output.setChildOutputName(dest.name())
outputs[name] = output
if dest.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput:
2018-04-09 08:28:34 +02:00
if dest.name() not in outputs:
output = QgsProcessingModelOutput(dest.name(), dest.name())
output.setChildId(alg.childId())
output.setChildOutputName(dest.name())
outputs[dest.name()] = output
2017-06-20 20:40:19 +10:00
alg.setModelOutputs(outputs)
2013-02-07 01:09:39 +01:00
2013-02-03 10:26:43 +01:00
selectedOptions = self.dependenciesPanel.selectedoptions
2017-01-23 09:24:31 +10:00
availableDependencies = self.getAvailableDependencies() # spellok
2017-06-13 16:05:59 +10:00
dep_ids = []
2013-02-03 10:26:43 +01:00
for selected in selectedOptions:
2017-06-13 16:05:59 +10:00
dep_ids.append(availableDependencies[selected].childId()) # spellok
alg.setDependencies(dep_ids)
2017-06-13 15:51:40 +10:00
#try:
# self._alg.processBeforeAddingToModeler(alg, self.model)
#except:
# pass
2013-02-07 01:09:39 +01:00
return alg
2012-09-15 18:25:25 +03:00
def okPressed(self):
alg = self.createAlgorithm()
if alg is not None:
self.accept()
2012-09-15 18:25:25 +03:00
def cancelPressed(self):
self.reject()
def openHelp(self):
algHelp = self._alg.helpUrl()
if not algHelp:
algHelp = QgsHelp.helpUrl("processing_algs/{}/{}.html#{}".format(
self._alg.provider().helpId(), self._alg.groupId(), "{}{}".format(self._alg.provider().helpId(), self._alg.name()))).toString()
if algHelp not in [None, ""]:
webbrowser.open(algHelp)