Skip to content

Commit

Permalink
Move parameter retrieval logic from dialog to ParametersPanel
Browse files Browse the repository at this point in the history
This is cleaner, because the panel owns all the wrappers required
to evaluate the parameter values
  • Loading branch information
nyalldawson committed Apr 4, 2020
1 parent a80bdc4 commit d69519a
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 96 deletions.
120 changes: 29 additions & 91 deletions python/plugins/processing/gui/AlgorithmDialog.py
Expand Up @@ -21,33 +21,21 @@
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'

import os
from pprint import pformat
import time

from qgis.PyQt.QtCore import QCoreApplication, Qt
from qgis.PyQt.QtWidgets import QMessageBox, QPushButton, QSizePolicy, QDialogButtonBox
from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtWidgets import QMessageBox, QPushButton, QDialogButtonBox
from qgis.PyQt.QtGui import QColor, QPalette

from qgis.core import (Qgis,
QgsProject,
QgsApplication,
QgsProcessingUtils,
QgsProcessingParameterDefinition,
QgsProcessingAlgRunnerTask,
QgsProcessingOutputHtml,
QgsProcessingParameterVectorDestination,
QgsProcessingOutputLayerDefinition,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterRasterDestination,
QgsProcessingAlgorithm,
QgsProcessingParameters,
QgsProxyProgressTask,
QgsTaskManager,
QgsProcessingFeatureSourceDefinition)
from qgis.gui import (QgsGui,
QgsMessageBar,
QgsProcessingLayerOutputDestinationWidget,
QgsProcessingAlgorithmDialogBase)
from qgis.utils import iface

Expand All @@ -59,7 +47,6 @@
from processing.gui.AlgorithmDialogBase import AlgorithmDialogBase
from processing.gui.AlgorithmExecutor import executeIterating, execute, execute_in_place
from processing.gui.Postprocessing import handleAlgorithmResults
from processing.gui.wrappers import WidgetWrapper

from processing.tools import dataobjects

Expand All @@ -82,20 +69,23 @@ def __init__(self, alg, in_place=False, parent=None):
if not self.in_place:
self.runAsBatchButton = QPushButton(QCoreApplication.translate("AlgorithmDialog", "Run as Batch Process…"))
self.runAsBatchButton.clicked.connect(self.runAsBatch)
self.buttonBox().addButton(self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment
self.buttonBox().addButton(self.runAsBatchButton,
QDialogButtonBox.ResetRole) # reset role to ensure left alignment
else:
self.active_layer = iface.activeLayer()
self.runAsBatchButton = None
has_selection = self.active_layer and (self.active_layer.selectedFeatureCount() > 0)
self.buttonBox().button(QDialogButtonBox.Ok).setText(QCoreApplication.translate("AlgorithmDialog", "Modify Selected Features")
if has_selection else QCoreApplication.translate("AlgorithmDialog", "Modify All Features"))
self.buttonBox().button(QDialogButtonBox.Close).setText(QCoreApplication.translate("AlgorithmDialog", "Cancel"))
self.buttonBox().button(QDialogButtonBox.Ok).setText(
QCoreApplication.translate("AlgorithmDialog", "Modify Selected Features")
if has_selection else QCoreApplication.translate("AlgorithmDialog", "Modify All Features"))
self.buttonBox().button(QDialogButtonBox.Close).setText(
QCoreApplication.translate("AlgorithmDialog", "Cancel"))
self.setWindowTitle(self.windowTitle() + ' | ' + self.active_layer.name())

self.updateRunButtonVisibility()

def getParametersPanel(self, alg, parent):
panel = ParametersPanel(parent, alg, self.in_place, parameters_generator=self)
panel = ParametersPanel(parent, alg, self.in_place)
return panel

def runAsBatch(self):
Expand All @@ -116,70 +106,10 @@ def setParameters(self, parameters):
self.mainWidget().setParameters(parameters)

def createProcessingParameters(self):
parameters = {}

if self.mainWidget() is None:
return parameters

for param in self.algorithm().parameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if not param.isDestination():

if self.in_place and param.name() == 'INPUT':
parameters[param.name()] = self.active_layer
continue

try:
wrapper = self.mainWidget().wrappers[param.name()]
except KeyError:
continue

# For compatibility with 3.x API, we need to check whether the wrapper is
# the deprecated WidgetWrapper class. If not, it's the newer
# QgsAbstractProcessingParameterWidgetWrapper class
# TODO QGIS 4.0 - remove
if issubclass(wrapper.__class__, WidgetWrapper):
widget = wrapper.widget
else:
widget = wrapper.wrappedWidget()

if widget is None:
continue

value = wrapper.parameterValue()
parameters[param.name()] = value

if not param.checkValueIsAcceptable(value):
raise AlgorithmDialogBase.InvalidParameterValue(param, widget)
else:
if self.in_place and param.name() == 'OUTPUT':
parameters[param.name()] = 'memory:'
continue

try:
wrapper = self.mainWidget().wrappers[param.name()]
except KeyError:
continue

widget = wrapper.wrappedWidget()
value = wrapper.parameterValue()

dest_project = None
if wrapper.customProperties().get('OPEN_AFTER_RUNNING'):
dest_project = QgsProject.instance()

if value and isinstance(value, QgsProcessingOutputLayerDefinition):
value.destinationProject = dest_project
if value:
parameters[param.name()] = value
if param.isDestination():
context = dataobjects.createContext()
ok, error = self.algorithm().provider().isSupportedOutputValue(value, param, context)
if not ok:
raise AlgorithmDialogBase.InvalidOutputExtension(widget, error)

return self.algorithm().preprocessParameters(parameters)
return {}
else:
return self.mainWidget().createProcessingParameters()

def runAlgorithm(self):
self.feedback = self.createFeedback()
Expand Down Expand Up @@ -211,7 +141,8 @@ def runAlgorithm(self):
self.iterateParam = None

for param in self.algorithm().parameterDefinitions():
if isinstance(parameters.get(param.name(), None), QgsProcessingFeatureSourceDefinition) and parameters[param.name()].flags & QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature:
if isinstance(parameters.get(param.name(), None), QgsProcessingFeatureSourceDefinition) and parameters[
param.name()].flags & QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature:
self.iterateParam = param.name()
break

Expand All @@ -223,12 +154,14 @@ def runAlgorithm(self):
self.setProgressText(QCoreApplication.translate('AlgorithmDialog', 'Processing algorithm…'))

self.setInfo(
QCoreApplication.translate('AlgorithmDialog', '<b>Algorithm \'{0}\' starting&hellip;</b>').format(self.algorithm().displayName()), escapeHtml=False)
QCoreApplication.translate('AlgorithmDialog', '<b>Algorithm \'{0}\' starting&hellip;</b>').format(
self.algorithm().displayName()), escapeHtml=False)

self.feedback.pushInfo(self.tr('Input parameters:'))
display_params = []
for k, v in parameters.items():
display_params.append("'" + k + "' : " + self.algorithm().parameterDefinition(k).valueAsPythonString(v, self.context))
display_params.append(
"'" + k + "' : " + self.algorithm().parameterDefinition(k).valueAsPythonString(v, self.context))
self.feedback.pushCommandInfo('{ ' + ', '.join(display_params) + ' }')
self.feedback.pushInfo('')
start_time = time.time()
Expand Down Expand Up @@ -259,7 +192,8 @@ def runAlgorithm(self):

def on_complete(ok, results):
if ok:
self.feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds').format(time.time() - start_time))
self.feedback.pushInfo(
self.tr('Execution completed in {0:0.2f} seconds').format(time.time() - start_time))
self.feedback.pushInfo(self.tr('Results:'))
r = {k: v for k, v in results.items() if k not in ('CHILD_RESULTS', 'CHILD_INPUTS')}
self.feedback.pushCommandInfo(pformat(r))
Expand Down Expand Up @@ -291,7 +225,9 @@ def on_complete(ok, results):
task.executed.connect(on_complete)
self.setCurrentTask(task)
else:
self.proxy_progress = QgsProxyProgressTask(QCoreApplication.translate("AlgorithmDialog", "Executing “{}”").format(self.algorithm().displayName()))
self.proxy_progress = QgsProxyProgressTask(
QCoreApplication.translate("AlgorithmDialog", "Executing “{}”").format(
self.algorithm().displayName()))
QgsApplication.taskManager().addTask(self.proxy_progress)
self.feedback.progressChanged.connect(self.proxy_progress.setProxyProgress)
self.feedback_dialog = self.createProgressDialog()
Expand All @@ -314,8 +250,9 @@ def on_complete(ok, results):
except:
pass
self.messageBar().clearWidgets()
self.messageBar().pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()),
level=Qgis.Warning, duration=5)
self.messageBar().pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(
e.parameter.description()),
level=Qgis.Warning, duration=5)
except AlgorithmDialogBase.InvalidOutputExtension as e:
try:
self.buttonBox().accepted.connect(lambda e=e:
Expand All @@ -337,7 +274,8 @@ def finish(self, successful, result, context, feedback, in_place=False):
# add html results to results dock
for out in self.algorithm().outputDefinitions():
if isinstance(out, QgsProcessingOutputHtml) and out.name() in result and result[out.name()]:
resultsList.addResult(icon=self.algorithm().icon(), name=out.description(), timestamp=time.localtime(),
resultsList.addResult(icon=self.algorithm().icon(), name=out.description(),
timestamp=time.localtime(),
result=result[out.name()])
if not handleAlgorithmResults(self.algorithm(), context, feedback, not keepOpen, result):
self.resetGui()
Expand Down
74 changes: 69 additions & 5 deletions python/plugins/processing/gui/ParametersPanel.py
Expand Up @@ -28,7 +28,8 @@
from qgis.core import (QgsProcessingParameterDefinition,
QgsProcessingParameterExtent,
QgsProject,
QgsProcessingModelAlgorithm)
QgsProcessingModelAlgorithm,
QgsProcessingOutputLayerDefinition)
from qgis.gui import (QgsProcessingContextGenerator,
QgsProcessingParameterWidgetContext,
QgsProcessingParametersWidget,
Expand All @@ -38,19 +39,19 @@
from qgis.utils import iface

from processing.gui.wrappers import WidgetWrapperFactory, WidgetWrapper
from processing.gui.AlgorithmDialogBase import AlgorithmDialogBase
from processing.tools.dataobjects import createContext


class ParametersPanel(QgsProcessingParametersWidget, QgsProcessingParametersGenerator):

def __init__(self, parent, alg, in_place=False, parameters_generator=None):
def __init__(self, parent, alg, in_place=False):
super().__init__(alg, parent)
self.in_place = in_place

self.wrappers = {}

self.processing_context = createContext()
self.processing_parameters_generator = parameters_generator

class ContextGenerator(QgsProcessingContextGenerator):

Expand Down Expand Up @@ -100,7 +101,7 @@ def initWidgets(self):
wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent())
wrapper.setWidgetContext(widget_context)
wrapper.registerProcessingContextGenerator(self.context_generator)
wrapper.registerProcessingParametersGenerator(self.processing_parameters_generator)
wrapper.registerProcessingParametersGenerator(self)
self.wrappers[param.name()] = wrapper

# For compatibility with 3.x API, we need to check whether the wrapper is
Expand Down Expand Up @@ -152,7 +153,7 @@ def initWidgets(self):
wrapper = QgsGui.processingGuiRegistry().createParameterWidgetWrapper(output, QgsProcessingGui.Standard)
wrapper.setWidgetContext(widget_context)
wrapper.registerProcessingContextGenerator(self.context_generator)
wrapper.registerProcessingParametersGenerator(self.processing_parameters_generator)
wrapper.registerProcessingParametersGenerator(self)
self.wrappers[output.name()] = wrapper

label = wrapper.createWrappedLabel()
Expand Down Expand Up @@ -185,6 +186,69 @@ def initWidgets(self):
for wrapper in list(self.wrappers.values()):
wrapper.postInitialize(list(self.wrappers.values()))

def createProcessingParameters(self):
parameters = {}

for param in self.algorithm().parameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if not param.isDestination():

if self.in_place and param.name() == 'INPUT':
parameters[param.name()] = self.active_layer
continue

try:
wrapper = self.wrappers[param.name()]
except KeyError:
continue

# For compatibility with 3.x API, we need to check whether the wrapper is
# the deprecated WidgetWrapper class. If not, it's the newer
# QgsAbstractProcessingParameterWidgetWrapper class
# TODO QGIS 4.0 - remove
if issubclass(wrapper.__class__, WidgetWrapper):
widget = wrapper.widget
else:
widget = wrapper.wrappedWidget()

if widget is None:
continue

value = wrapper.parameterValue()
parameters[param.name()] = value

if not param.checkValueIsAcceptable(value):
raise AlgorithmDialogBase.InvalidParameterValue(param, widget)
else:
if self.in_place and param.name() == 'OUTPUT':
parameters[param.name()] = 'memory:'
continue

try:
wrapper = self.wrappers[param.name()]
except KeyError:
continue

widget = wrapper.wrappedWidget()
value = wrapper.parameterValue()

dest_project = None
if wrapper.customProperties().get('OPEN_AFTER_RUNNING'):
dest_project = QgsProject.instance()

if value and isinstance(value, QgsProcessingOutputLayerDefinition):
value.destinationProject = dest_project
if value:
parameters[param.name()] = value
if param.isDestination():
context = createContext()
ok, error = self.algorithm().provider().isSupportedOutputValue(value, param, context)
if not ok:
raise AlgorithmDialogBase.InvalidOutputExtension(widget, error)

return self.algorithm().preprocessParameters(parameters)

def setParameters(self, parameters):
for param in self.algorithm().parameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
Expand Down

0 comments on commit d69519a

Please sign in to comment.