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