1# -*- coding: utf-8 -*- 2 3""" 4*************************************************************************** 5 ParametersPanel.py 6 --------------------- 7 Date : August 2012 8 Copyright : (C) 2012 by Victor Olaya 9 (C) 2013 by CS Systemes d'information (CS SI) 10 Email : volayaf at gmail dot com 11 otb at c-s dot fr (CS SI) 12 Contributors : Victor Olaya 13 14*************************************************************************** 15* * 16* This program is free software; you can redistribute it and/or modify * 17* it under the terms of the GNU General Public License as published by * 18* the Free Software Foundation; either version 2 of the License, or * 19* (at your option) any later version. * 20* * 21*************************************************************************** 22""" 23 24__author__ = 'Victor Olaya' 25__date__ = 'August 2012' 26__copyright__ = '(C) 2012, Victor Olaya' 27 28from qgis.core import (QgsProcessingParameterDefinition, 29 QgsProcessingParameterExtent, 30 QgsProject, 31 QgsProcessingModelAlgorithm, 32 QgsProcessingOutputLayerDefinition) 33from qgis.gui import (QgsProcessingContextGenerator, 34 QgsProcessingParameterWidgetContext, 35 QgsProcessingParametersWidget, 36 QgsGui, 37 QgsProcessingGui, 38 QgsProcessingParametersGenerator, 39 QgsProcessingHiddenWidgetWrapper) 40from qgis.utils import iface 41 42from processing.gui.wrappers import WidgetWrapperFactory, WidgetWrapper 43from processing.gui.AlgorithmDialogBase import AlgorithmDialogBase 44from processing.tools.dataobjects import createContext 45 46 47class ParametersPanel(QgsProcessingParametersWidget): 48 49 def __init__(self, parent, alg, in_place=False, active_layer=None): 50 super().__init__(alg, parent) 51 self.in_place = in_place 52 self.active_layer = active_layer 53 54 self.wrappers = {} 55 56 self.extra_parameters = {} 57 58 self.processing_context = createContext() 59 60 class ContextGenerator(QgsProcessingContextGenerator): 61 62 def __init__(self, context): 63 super().__init__() 64 self.processing_context = context 65 66 def processingContext(self): 67 return self.processing_context 68 69 self.context_generator = ContextGenerator(self.processing_context) 70 71 self.initWidgets() 72 73 QgsProject.instance().layerWasAdded.connect(self.layerRegistryChanged) 74 QgsProject.instance().layersWillBeRemoved.connect(self.layerRegistryChanged) 75 76 def layerRegistryChanged(self, layers): 77 for wrapper in list(self.wrappers.values()): 78 try: 79 wrapper.refresh() 80 except AttributeError: 81 pass 82 83 def initWidgets(self): 84 super().initWidgets() 85 86 widget_context = QgsProcessingParameterWidgetContext() 87 widget_context.setProject(QgsProject.instance()) 88 if iface is not None: 89 widget_context.setMapCanvas(iface.mapCanvas()) 90 widget_context.setBrowserModel(iface.browserModel()) 91 widget_context.setActiveLayer(iface.activeLayer()) 92 93 widget_context.setMessageBar(self.parent().messageBar()) 94 if isinstance(self.algorithm(), QgsProcessingModelAlgorithm): 95 widget_context.setModel(self.algorithm()) 96 97 in_place_input_parameter_name = 'INPUT' 98 if hasattr(self.algorithm(), 'inputParameterName'): 99 in_place_input_parameter_name = self.algorithm().inputParameterName() 100 101 # Create widgets and put them in layouts 102 for param in self.algorithm().parameterDefinitions(): 103 if param.flags() & QgsProcessingParameterDefinition.FlagHidden: 104 continue 105 106 if param.isDestination(): 107 continue 108 else: 109 if self.in_place and param.name() in (in_place_input_parameter_name, 'OUTPUT'): 110 # don't show the input/output parameter widgets in in-place mode 111 # we still need to CREATE them, because other wrappers may need to interact 112 # with them (e.g. those parameters which need the input layer for field 113 # selections/crs properties/etc) 114 self.wrappers[param.name()] = QgsProcessingHiddenWidgetWrapper(param, QgsProcessingGui.Standard, self) 115 self.wrappers[param.name()].setLinkedVectorLayer(self.active_layer) 116 continue 117 118 wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent()) 119 wrapper.setWidgetContext(widget_context) 120 wrapper.registerProcessingContextGenerator(self.context_generator) 121 wrapper.registerProcessingParametersGenerator(self) 122 self.wrappers[param.name()] = wrapper 123 124 # For compatibility with 3.x API, we need to check whether the wrapper is 125 # the deprecated WidgetWrapper class. If not, it's the newer 126 # QgsAbstractProcessingParameterWidgetWrapper class 127 # TODO QGIS 4.0 - remove 128 is_python_wrapper = issubclass(wrapper.__class__, WidgetWrapper) 129 stretch = 0 130 if not is_python_wrapper: 131 widget = wrapper.createWrappedWidget(self.processing_context) 132 stretch = wrapper.stretch() 133 else: 134 widget = wrapper.widget 135 136 if widget is not None: 137 if is_python_wrapper: 138 widget.setToolTip(param.toolTip()) 139 140 label = None 141 if not is_python_wrapper: 142 label = wrapper.createWrappedLabel() 143 else: 144 label = wrapper.label 145 146 if label is not None: 147 self.addParameterLabel(param, label) 148 elif is_python_wrapper: 149 desc = param.description() 150 if isinstance(param, QgsProcessingParameterExtent): 151 desc += self.tr(' (xmin, xmax, ymin, ymax)') 152 if param.flags() & QgsProcessingParameterDefinition.FlagOptional: 153 desc += self.tr(' [optional]') 154 widget.setText(desc) 155 156 self.addParameterWidget(param, widget, stretch) 157 158 for output in self.algorithm().destinationParameterDefinitions(): 159 if output.flags() & QgsProcessingParameterDefinition.FlagHidden: 160 continue 161 162 if self.in_place and output.name() in (in_place_input_parameter_name, 'OUTPUT'): 163 continue 164 165 wrapper = QgsGui.processingGuiRegistry().createParameterWidgetWrapper(output, QgsProcessingGui.Standard) 166 wrapper.setWidgetContext(widget_context) 167 wrapper.registerProcessingContextGenerator(self.context_generator) 168 wrapper.registerProcessingParametersGenerator(self) 169 self.wrappers[output.name()] = wrapper 170 171 label = wrapper.createWrappedLabel() 172 if label is not None: 173 self.addOutputLabel(label) 174 175 widget = wrapper.createWrappedWidget(self.processing_context) 176 self.addOutputWidget(widget, wrapper.stretch()) 177 178 # def skipOutputChanged(widget, checkbox, skipped): 179 # TODO 180 # enabled = not skipped 181 # 182 # # Do not try to open formats that are write-only. 183 # value = widget.value() 184 # if value and isinstance(value, QgsProcessingOutputLayerDefinition) and isinstance(output, ( 185 # QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): 186 # filename = value.sink.staticValue() 187 # if filename not in ('memory:', ''): 188 # path, ext = os.path.splitext(filename) 189 # format = QgsVectorFileWriter.driverForExtension(ext) 190 # drv = gdal.GetDriverByName(format) 191 # if drv: 192 # if drv.GetMetadataItem(gdal.DCAP_OPEN) is None: 193 # enabled = False 194 # 195 # checkbox.setEnabled(enabled) 196 # checkbox.setChecked(enabled) 197 198 for wrapper in list(self.wrappers.values()): 199 wrapper.postInitialize(list(self.wrappers.values())) 200 201 def createProcessingParameters(self): 202 parameters = {} 203 for p, v in self.extra_parameters.items(): 204 parameters[p] = v 205 206 for param in self.algorithm().parameterDefinitions(): 207 if param.flags() & QgsProcessingParameterDefinition.FlagHidden: 208 continue 209 if not param.isDestination(): 210 try: 211 wrapper = self.wrappers[param.name()] 212 except KeyError: 213 continue 214 215 # For compatibility with 3.x API, we need to check whether the wrapper is 216 # the deprecated WidgetWrapper class. If not, it's the newer 217 # QgsAbstractProcessingParameterWidgetWrapper class 218 # TODO QGIS 4.0 - remove 219 if issubclass(wrapper.__class__, WidgetWrapper): 220 widget = wrapper.widget 221 else: 222 widget = wrapper.wrappedWidget() 223 224 if not isinstance(wrapper, QgsProcessingHiddenWidgetWrapper) and widget is None: 225 continue 226 227 value = wrapper.parameterValue() 228 parameters[param.name()] = value 229 230 if not param.checkValueIsAcceptable(value): 231 raise AlgorithmDialogBase.InvalidParameterValue(param, widget) 232 else: 233 if self.in_place and param.name() == 'OUTPUT': 234 parameters[param.name()] = 'memory:' 235 continue 236 237 try: 238 wrapper = self.wrappers[param.name()] 239 except KeyError: 240 continue 241 242 widget = wrapper.wrappedWidget() 243 value = wrapper.parameterValue() 244 245 dest_project = None 246 if wrapper.customProperties().get('OPEN_AFTER_RUNNING'): 247 dest_project = QgsProject.instance() 248 249 if value and isinstance(value, QgsProcessingOutputLayerDefinition): 250 value.destinationProject = dest_project 251 if value: 252 parameters[param.name()] = value 253 254 context = createContext() 255 ok, error = param.isSupportedOutputValue(value, context) 256 if not ok: 257 raise AlgorithmDialogBase.InvalidOutputExtension(widget, error) 258 259 return self.algorithm().preprocessParameters(parameters) 260 261 def setParameters(self, parameters): 262 self.extra_parameters = {} 263 for param in self.algorithm().parameterDefinitions(): 264 if param.flags() & QgsProcessingParameterDefinition.FlagHidden: 265 if param.name() in parameters: 266 self.extra_parameters[param.name()] = parameters[param.name()] 267 continue 268 269 if not param.name() in parameters: 270 continue 271 272 value = parameters[param.name()] 273 274 wrapper = self.wrappers[param.name()] 275 wrapper.setParameterValue(value, self.processing_context) 276