123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- """
- ***************************************************************************
- 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. *
- * *
- ***************************************************************************
- """
- __author__ = 'Victor Olaya'
- __date__ = 'August 2012'
- __copyright__ = '(C) 2012, Victor Olaya'
- import webbrowser
- from qgis.PyQt.QtCore import Qt
- from qgis.PyQt.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QLineEdit,
- QFrame, QPushButton, QSizePolicy, QVBoxLayout,
- QHBoxLayout, QWidget, QTabWidget, QTextEdit)
- from qgis.PyQt.QtGui import QColor
- from qgis.core import (Qgis,
- QgsProject,
- QgsProcessingParameterDefinition,
- QgsProcessingModelOutput,
- QgsProcessingModelChildAlgorithm,
- QgsProcessingModelChildParameterSource,
- QgsProcessingOutputDefinition)
- from qgis.gui import (QgsGui,
- QgsMessageBar,
- QgsScrollArea,
- QgsFilterLineEdit,
- QgsHelp,
- QgsProcessingContextGenerator,
- QgsProcessingModelerParameterWidget,
- QgsProcessingParameterWidgetContext,
- QgsPanelWidget,
- QgsPanelWidgetStack,
- QgsColorButton,
- QgsModelChildDependenciesWidget)
- from qgis.utils import iface
- from processing.gui.wrappers import WidgetWrapperFactory
- from processing.gui.wrappers import InvalidParameterValue
- from processing.tools.dataobjects import createContext
- from processing.gui.wrappers import WidgetWrapper
- class ModelerParametersDialog(QDialog):
- def __init__(self, alg, model, algName=None, configuration=None):
- super().__init__()
- self.setObjectName('ModelerParametersDialog')
- self.setModal(True)
- if iface is not None:
- self.setStyleSheet(iface.mainWindow().styleSheet())
- # dammit this is SUCH as mess... stupid stable API
- 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.setWindowTitle(self._alg.displayName())
- self.widget = ModelerParametersWidget(alg, model, algName, configuration, context=self.context, dialog=self)
- QgsGui.enableAutoGeometryRestore(self)
- self.buttonBox = QDialogButtonBox()
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help)
- self.buttonBox.accepted.connect(self.okPressed)
- self.buttonBox.rejected.connect(self.reject)
- self.buttonBox.helpRequested.connect(self.openHelp)
- mainLayout = QVBoxLayout()
- mainLayout.addWidget(self.widget, 1)
- mainLayout.addWidget(self.buttonBox)
- self.setLayout(mainLayout)
- def algorithm(self):
- return self._alg
- def setComments(self, text):
- self.widget.setComments(text)
- def comments(self):
- return self.widget.comments()
- def setCommentColor(self, color):
- self.widget.setCommentColor(color)
- def commentColor(self):
- return self.widget.commentColor()
- def switchToCommentTab(self):
- self.widget.switchToCommentTab()
- def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]):
- # upgrade paramType to list
- if paramType is None:
- paramType = []
- elif not isinstance(paramType, (tuple, list)):
- paramType = [paramType]
- if outTypes is None:
- outTypes = []
- elif not isinstance(outTypes, (tuple, list)):
- outTypes = [outTypes]
- 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)
- def resolveValueDescription(self, value):
- if isinstance(value, QgsProcessingModelChildParameterSource):
- if value.source() == Qgis.ProcessingModelChildParameterSource.StaticValue:
- return value.staticValue()
- elif value.source() == Qgis.ProcessingModelChildParameterSource.ModelParameter:
- return self.model.parameterDefinition(value.parameterName()).description()
- elif value.source() == Qgis.ProcessingModelChildParameterSource.ChildOutput:
- alg = self.model.childAlgorithm(value.outputChildId())
- output_name = alg.algorithm().outputDefinition(value.outputName()).description()
- # see if this output has been named by the model designer -- if so, we use that friendly name
- for name, output in alg.modelOutputs().items():
- if output.childOutputName() == value.outputName():
- output_name = name
- break
- return self.tr("'{0}' from algorithm '{1}'").format(output_name, alg.description())
- return value
- def setPreviousValues(self):
- self.widget.setPreviousValues()
- def createAlgorithm(self):
- return self.widget.createAlgorithm()
- def okPressed(self):
- if self.createAlgorithm() is not None:
- self.accept()
- def openHelp(self):
- algHelp = self.widget.algorithm().helpUrl()
- if not algHelp:
- algHelp = QgsHelp.helpUrl("processing_algs/{}/{}.html#{}".format(
- self.widget.algorithm().provider().helpId(), self.algorithm().groupId(),
- f"{self.algorithm().provider().helpId()}{self.algorithm().name()}")).toString()
- if algHelp not in [None, ""]:
- webbrowser.open(algHelp)
- class ModelerParametersPanelWidget(QgsPanelWidget):
- def __init__(self, alg, model, algName=None, configuration=None, dialog=None, context=None):
- super().__init__()
- 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 = context
- self.dialog = dialog
- self.widget_labels = {}
- self.previous_output_definitions = {}
- 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)
- self.setupUi()
- self.params = None
- def algorithm(self):
- return self._alg
- def setupUi(self):
- self.showAdvanced = False
- self.wrappers = {}
- self.algorithmItem = None
- self.mainLayout = QVBoxLayout()
- self.mainLayout.setContentsMargins(0, 0, 0, 0)
- self.verticalLayout = QVBoxLayout()
- self.bar = QgsMessageBar()
- self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
- self.verticalLayout.addWidget(self.bar)
- hLayout = QHBoxLayout()
- hLayout.setContentsMargins(0, 0, 0, 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)
- widget_context = QgsProcessingParameterWidgetContext()
- widget_context.setProject(QgsProject.instance())
- if iface is not None:
- widget_context.setMapCanvas(iface.mapCanvas())
- widget_context.setActiveLayer(iface.activeLayer())
- widget_context.setModel(self.model)
- widget_context.setModelChildAlgorithmId(self.childId)
- 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)
- for param in self._alg.parameterDefinitions():
- 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
- for param in self._alg.parameterDefinitions():
- if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden:
- continue
- wrapper = WidgetWrapperFactory.create_wrapper(param, self.dialog)
- 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:
- if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget):
- label = wrapper.createLabel()
- else:
- tooltip = param.description()
- widget.setToolTip(tooltip)
- label = wrapper.label
- self.widget_labels[param.name()] = label
- if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced:
- label.setVisible(self.showAdvanced)
- widget.setVisible(self.showAdvanced)
- self.verticalLayout.addWidget(label)
- self.verticalLayout.addWidget(widget)
- for output in self._alg.destinationParameterDefinitions():
- if output.flags() & QgsProcessingParameterDefinition.FlagHidden:
- continue
- widget = QgsGui.processingGuiRegistry().createModelerParameterWidget(self.model,
- self.childId,
- output,
- self.context)
- widget.setDialog(self.dialog)
- widget.setWidgetContext(widget_context)
- widget.registerProcessingContextGenerator(self.context_generator)
- self.wrappers[output.name()] = widget
- item = QgsFilterLineEdit()
- if hasattr(item, 'setPlaceholderText'):
- item.setPlaceholderText(self.tr('[Enter name if this is a final result]'))
- label = widget.createLabel()
- if label is not None:
- self.verticalLayout.addWidget(label)
- self.verticalLayout.addWidget(widget)
- label = QLabel(' ')
- self.verticalLayout.addWidget(label)
- label = QLabel(self.tr('Dependencies'))
- self.dependencies_panel = QgsModelChildDependenciesWidget(self, self.model, self.childId)
- self.verticalLayout.addWidget(label)
- self.verticalLayout.addWidget(self.dependencies_panel)
- self.verticalLayout.addStretch(1000)
- self.setPreviousValues()
- 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)
- self.scrollArea.setFrameStyle(QFrame.NoFrame)
- self.verticalLayout2.addWidget(self.scrollArea)
- w = QWidget()
- w.setLayout(self.verticalLayout2)
- self.mainLayout.addWidget(w)
- self.setLayout(self.mainLayout)
- 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'))
- for param in self._alg.parameterDefinitions():
- 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)
- def setPreviousValues(self):
- if self.childId is not None:
- alg = self.model.childAlgorithm(self.childId)
- self.descriptionBox.setText(alg.description())
- for param in alg.algorithm().parameterDefinitions():
- if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden:
- continue
- value = None
- 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
- 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() == Qgis.ProcessingModelChildParameterSource.StaticValue:
- value = value.staticValue()
- wrapper.setValue(value)
- for output in self.algorithm().destinationParameterDefinitions():
- if output.flags() & QgsProcessingParameterDefinition.FlagHidden:
- continue
- model_output_name = None
- for name, out in alg.modelOutputs().items():
- if out.childId() == self.childId and out.childOutputName() == output.name():
- # this destination parameter is linked to a model output
- model_output_name = out.name()
- self.previous_output_definitions[output.name()] = out
- break
- value = None
- if model_output_name is None and output.name() in alg.parameterSources():
- value = alg.parameterSources()[output.name()]
- if isinstance(value, list) and len(value) == 1:
- value = value[0]
- elif isinstance(value, list) and len(value) == 0:
- value = None
- wrapper = self.wrappers[output.name()]
- if model_output_name is not None:
- wrapper.setToModelOutput(model_output_name)
- elif value is not None or output.defaultValue() is not None:
- if value is None:
- value = QgsProcessingModelChildParameterSource.fromStaticValue(output.defaultValue())
- wrapper.setWidgetValue(value)
- self.dependencies_panel.setValue(alg.dependencies())
- def createAlgorithm(self):
- alg = QgsProcessingModelChildAlgorithm(self._alg.id())
- if not self.childId:
- alg.generateChildId(self.model)
- else:
- alg.setChildId(self.childId)
- alg.setDescription(self.descriptionBox.text())
- if self.algorithmItem:
- alg.setConfiguration(self.algorithmItem.configuration())
- self._alg = alg.algorithm().create(self.algorithmItem.configuration())
- for param in self._alg.parameterDefinitions():
- if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden:
- 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:
- val = None
- if isinstance(val, QgsProcessingModelChildParameterSource):
- val = [val]
- elif not (isinstance(val, list) and all(
- [isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])):
- val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)]
- valid = True
- for subval in val:
- if (isinstance(subval, QgsProcessingModelChildParameterSource)
- and subval.source() == Qgis.ProcessingModelChildParameterSource.StaticValue
- and not param.checkValueIsAcceptable(subval.staticValue())) \
- or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional):
- valid = False
- break
- if valid:
- alg.addParameterSources(param.name(), val)
- outputs = {}
- for output in self._alg.destinationParameterDefinitions():
- if not output.flags() & QgsProcessingParameterDefinition.FlagHidden:
- wrapper = self.wrappers[output.name()]
- if wrapper.isModelOutput():
- name = wrapper.modelOutputName()
- if name:
- # if there was a previous output definition already for this output, we start with it,
- # otherwise we'll lose any existing output comments, coloring, position, etc
- model_output = self.previous_output_definitions.get(output.name(), QgsProcessingModelOutput(name, name))
- model_output.setDescription(name)
- model_output.setChildId(alg.childId())
- model_output.setChildOutputName(output.name())
- outputs[name] = model_output
- else:
- val = wrapper.value()
- if isinstance(val, QgsProcessingModelChildParameterSource):
- val = [val]
- alg.addParameterSources(output.name(), val)
- if output.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput:
- if output.name() not in outputs:
- model_output = QgsProcessingModelOutput(output.name(), output.name())
- model_output.setChildId(alg.childId())
- model_output.setChildOutputName(output.name())
- outputs[output.name()] = model_output
- alg.setModelOutputs(outputs)
- alg.setDependencies(self.dependencies_panel.value())
- return alg
- class ModelerParametersWidget(QWidget):
- def __init__(self, alg, model, algName=None, configuration=None, dialog=None, context=None):
- super().__init__()
- 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 = context
- self.dialog = dialog
- self.widget = ModelerParametersPanelWidget(alg, model, algName, configuration, dialog, context)
- 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)
- self.setupUi()
- self.params = None
- def algorithm(self):
- return self._alg
- def switchToCommentTab(self):
- self.tab.setCurrentIndex(1)
- self.commentEdit.setFocus()
- self.commentEdit.selectAll()
- def setupUi(self):
- self.mainLayout = QVBoxLayout()
- self.mainLayout.setContentsMargins(0, 0, 0, 0)
- self.tab = QTabWidget()
- self.mainLayout.addWidget(self.tab)
- self.param_widget = QgsPanelWidgetStack()
- self.widget.setDockMode(True)
- self.param_widget.setMainPanel(self.widget)
- self.tab.addTab(self.param_widget, self.tr('Properties'))
- self.commentLayout = QVBoxLayout()
- self.commentEdit = QTextEdit()
- self.commentEdit.setAcceptRichText(False)
- self.commentLayout.addWidget(self.commentEdit, 1)
- hl = QHBoxLayout()
- hl.setContentsMargins(0, 0, 0, 0)
- hl.addWidget(QLabel(self.tr('Color')))
- self.comment_color_button = QgsColorButton()
- self.comment_color_button.setAllowOpacity(True)
- self.comment_color_button.setWindowTitle(self.tr('Comment Color'))
- self.comment_color_button.setShowNull(True, self.tr('Default'))
- hl.addWidget(self.comment_color_button)
- self.commentLayout.addLayout(hl)
- w2 = QWidget()
- w2.setLayout(self.commentLayout)
- self.tab.addTab(w2, self.tr('Comments'))
- self.setLayout(self.mainLayout)
- def setComments(self, text):
- self.commentEdit.setPlainText(text)
- def comments(self):
- return self.commentEdit.toPlainText()
- def setCommentColor(self, color):
- if color.isValid():
- self.comment_color_button.setColor(color)
- else:
- self.comment_color_button.setToNull()
- def commentColor(self):
- return self.comment_color_button.color() if not self.comment_color_button.isNull() else QColor()
- def setPreviousValues(self):
- self.widget.setPreviousValues()
- def createAlgorithm(self):
- alg = self.widget.createAlgorithm()
- if alg:
- alg.comment().setDescription(self.comments())
- alg.comment().setColor(self.commentColor())
- return alg
|