""" *************************************************************************** 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)