mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-09 00:08:52 -04:00
Adds an API which an algorithm can implement to support auto-setting parameter values. This is designed to handle the case of eg an algorithm which does a file format translation, where it's desirable to default the output parameter value to an input parameter value with a different extension. This can now be done by implementing autogenerateParameterValues in the algorithm, eg: def autogenerateParameterValues(self, existingParameters, changedParameter, mode): if changedParameter == self.INPUT: input_file = existingParameters.get(self.INPUT) if input_file: input_path = Path(input_file) if input_path.exists(): # auto set output parameter to same as input but with 'qgs' extension return {self.OUTPUT: input_path.with_suffix('.qgs').as_posix()} return {} Works for both toolbox and batch modes for algorithms
322 lines
13 KiB
Python
322 lines
13 KiB
Python
"""
|
|
***************************************************************************
|
|
ParametersPanel.py
|
|
---------------------
|
|
Date : August 2012
|
|
Copyright : (C) 2012 by Victor Olaya
|
|
(C) 2013 by CS Systemes d'information (CS SI)
|
|
Email : volayaf at gmail dot com
|
|
otb at c-s dot fr (CS SI)
|
|
Contributors : Victor Olaya
|
|
|
|
***************************************************************************
|
|
* *
|
|
* 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"
|
|
|
|
from qgis.core import (
|
|
Qgis,
|
|
QgsProcessingParameterDefinition,
|
|
QgsProcessingParameterExtent,
|
|
QgsProject,
|
|
QgsProcessingModelAlgorithm,
|
|
QgsProcessingOutputLayerDefinition,
|
|
)
|
|
from qgis.gui import (
|
|
QgsProcessingContextGenerator,
|
|
QgsProcessingParameterWidgetContext,
|
|
QgsProcessingParametersWidget,
|
|
QgsGui,
|
|
QgsProcessingGui,
|
|
QgsProcessingParametersGenerator,
|
|
QgsProcessingHiddenWidgetWrapper,
|
|
QgsAbstractProcessingParameterWidgetWrapper,
|
|
)
|
|
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):
|
|
|
|
def __init__(self, parent, alg, in_place=False, active_layer=None):
|
|
super().__init__(alg, parent)
|
|
self.in_place = in_place
|
|
self.active_layer = active_layer
|
|
|
|
self.wrappers = {}
|
|
|
|
self.extra_parameters = {}
|
|
|
|
self.processing_context = createContext()
|
|
|
|
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.processing_context)
|
|
|
|
self.initWidgets()
|
|
|
|
QgsProject.instance().layerWasAdded.connect(self.layerRegistryChanged)
|
|
QgsProject.instance().layersWillBeRemoved.connect(self.layerRegistryChanged)
|
|
|
|
def layerRegistryChanged(self, layers):
|
|
for wrapper in list(self.wrappers.values()):
|
|
try:
|
|
wrapper.refresh()
|
|
except AttributeError:
|
|
pass
|
|
|
|
def initWidgets(self):
|
|
super().initWidgets()
|
|
|
|
widget_context = QgsProcessingParameterWidgetContext()
|
|
widget_context.setProject(QgsProject.instance())
|
|
if iface is not None:
|
|
widget_context.setMapCanvas(iface.mapCanvas())
|
|
widget_context.setBrowserModel(iface.browserModel())
|
|
widget_context.setActiveLayer(iface.activeLayer())
|
|
|
|
widget_context.setMessageBar(self.parent().messageBar())
|
|
if isinstance(self.algorithm(), QgsProcessingModelAlgorithm):
|
|
widget_context.setModel(self.algorithm())
|
|
|
|
in_place_input_parameter_name = "INPUT"
|
|
if hasattr(self.algorithm(), "inputParameterName"):
|
|
in_place_input_parameter_name = self.algorithm().inputParameterName()
|
|
|
|
# Create widgets and put them in layouts
|
|
for param in self.algorithm().parameterDefinitions():
|
|
if param.flags() & QgsProcessingParameterDefinition.Flag.FlagHidden:
|
|
continue
|
|
|
|
if param.isDestination():
|
|
continue
|
|
else:
|
|
if self.in_place and param.name() in (
|
|
in_place_input_parameter_name,
|
|
"OUTPUT",
|
|
):
|
|
# don't show the input/output parameter widgets in in-place mode
|
|
# we still need to CREATE them, because other wrappers may need to interact
|
|
# with them (e.g. those parameters which need the input layer for field
|
|
# selections/crs properties/etc)
|
|
self.wrappers[param.name()] = QgsProcessingHiddenWidgetWrapper(
|
|
param, QgsProcessingGui.WidgetType.Standard, self
|
|
)
|
|
self.wrappers[param.name()].setLinkedVectorLayer(self.active_layer)
|
|
continue
|
|
|
|
wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent())
|
|
wrapper.setWidgetContext(widget_context)
|
|
wrapper.registerProcessingContextGenerator(self.context_generator)
|
|
wrapper.registerProcessingParametersGenerator(self)
|
|
self.wrappers[param.name()] = wrapper
|
|
|
|
# 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
|
|
is_python_wrapper = issubclass(wrapper.__class__, WidgetWrapper)
|
|
stretch = 0
|
|
if not is_python_wrapper:
|
|
widget = wrapper.createWrappedWidget(self.processing_context)
|
|
wrapper.widgetValueHasChanged.connect(self.parameterChanged)
|
|
stretch = wrapper.stretch()
|
|
else:
|
|
widget = wrapper.widget
|
|
|
|
if widget is not None:
|
|
if is_python_wrapper:
|
|
widget.setToolTip(param.toolTip())
|
|
|
|
label = None
|
|
if not is_python_wrapper:
|
|
label = wrapper.createWrappedLabel()
|
|
else:
|
|
label = wrapper.label
|
|
|
|
if label is not None:
|
|
self.addParameterLabel(param, label)
|
|
elif is_python_wrapper:
|
|
desc = param.description()
|
|
if isinstance(param, QgsProcessingParameterExtent):
|
|
desc += self.tr(" (xmin, xmax, ymin, ymax)")
|
|
if (
|
|
param.flags()
|
|
& QgsProcessingParameterDefinition.Flag.FlagOptional
|
|
):
|
|
desc += self.tr(" [optional]")
|
|
widget.setText(desc)
|
|
|
|
self.addParameterWidget(param, widget, stretch)
|
|
|
|
for output in self.algorithm().destinationParameterDefinitions():
|
|
if output.flags() & QgsProcessingParameterDefinition.Flag.FlagHidden:
|
|
continue
|
|
|
|
if self.in_place and output.name() in (
|
|
in_place_input_parameter_name,
|
|
"OUTPUT",
|
|
):
|
|
continue
|
|
|
|
wrapper = QgsGui.processingGuiRegistry().createParameterWidgetWrapper(
|
|
output, QgsProcessingGui.WidgetType.Standard
|
|
)
|
|
wrapper.setWidgetContext(widget_context)
|
|
wrapper.registerProcessingContextGenerator(self.context_generator)
|
|
wrapper.registerProcessingParametersGenerator(self)
|
|
self.wrappers[output.name()] = wrapper
|
|
|
|
label = wrapper.createWrappedLabel()
|
|
if label is not None:
|
|
self.addOutputLabel(label)
|
|
|
|
widget = wrapper.createWrappedWidget(self.processing_context)
|
|
self.addOutputWidget(widget, wrapper.stretch())
|
|
|
|
# def skipOutputChanged(widget, checkbox, skipped):
|
|
# TODO
|
|
# enabled = not skipped
|
|
#
|
|
# # Do not try to open formats that are write-only.
|
|
# value = widget.value()
|
|
# if value and isinstance(value, QgsProcessingOutputLayerDefinition) and isinstance(output, (
|
|
# QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)):
|
|
# filename = value.sink.staticValue()
|
|
# if filename not in ('memory:', ''):
|
|
# path, ext = os.path.splitext(filename)
|
|
# format = QgsVectorFileWriter.driverForExtension(ext)
|
|
# drv = gdal.GetDriverByName(format)
|
|
# if drv:
|
|
# if drv.GetMetadataItem(gdal.DCAP_OPEN) is None:
|
|
# enabled = False
|
|
#
|
|
# checkbox.setEnabled(enabled)
|
|
# checkbox.setChecked(enabled)
|
|
|
|
for wrapper in list(self.wrappers.values()):
|
|
wrapper.postInitialize(list(self.wrappers.values()))
|
|
|
|
def createProcessingParameters(
|
|
self, flags=QgsProcessingParametersGenerator.Flags()
|
|
):
|
|
include_default = not (
|
|
flags & QgsProcessingParametersGenerator.Flag.SkipDefaultValueParameters
|
|
)
|
|
validate = not (flags & QgsProcessingParametersGenerator.Flag.SkipValidation)
|
|
parameters = {}
|
|
for p, v in self.extra_parameters.items():
|
|
parameters[p] = v
|
|
|
|
for param in self.algorithm().parameterDefinitions():
|
|
if param.flags() & QgsProcessingParameterDefinition.Flag.FlagHidden:
|
|
continue
|
|
if not param.isDestination():
|
|
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 (
|
|
not isinstance(wrapper, QgsProcessingHiddenWidgetWrapper)
|
|
and widget is None
|
|
):
|
|
continue
|
|
|
|
value = wrapper.parameterValue()
|
|
if param.defaultValue() != value or include_default:
|
|
parameters[param.name()] = value
|
|
|
|
if validate and 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 and (param.defaultValue() != value or include_default):
|
|
parameters[param.name()] = value
|
|
|
|
context = createContext()
|
|
if validate:
|
|
ok, error = param.isSupportedOutputValue(value, context)
|
|
if not ok:
|
|
raise AlgorithmDialogBase.InvalidOutputExtension(
|
|
widget, error
|
|
)
|
|
|
|
return self.algorithm().preprocessParameters(parameters)
|
|
|
|
def setParameters(self, parameters):
|
|
self.extra_parameters = {}
|
|
for param in self.algorithm().parameterDefinitions():
|
|
if param.flags() & QgsProcessingParameterDefinition.Flag.FlagHidden:
|
|
if param.name() in parameters:
|
|
self.extra_parameters[param.name()] = parameters[param.name()]
|
|
continue
|
|
|
|
if not param.name() in parameters:
|
|
continue
|
|
|
|
value = parameters[param.name()]
|
|
|
|
wrapper = self.wrappers[param.name()]
|
|
wrapper.setParameterValue(value, self.processing_context)
|
|
|
|
def parameterChanged(self):
|
|
"""
|
|
Called when a parameter value is changed in the panel
|
|
"""
|
|
wrapper: QgsAbstractProcessingParameterWidgetWrapper = self.sender()
|
|
default_values = self.algorithm().autogenerateParameterValues(
|
|
self.createProcessingParameters(
|
|
QgsProcessingParametersGenerator.Flag.SkipValidation
|
|
),
|
|
wrapper.parameterDefinition().name(),
|
|
Qgis.ProcessingMode.Standard,
|
|
)
|
|
if default_values:
|
|
self.setParameters(default_values)
|