Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #6504 from m-kuhn/processingParametersRegistry
More processing parameters in modeller
  • Loading branch information
m-kuhn committed Mar 1, 2018
2 parents 9e955ce + b67813b commit 4f4b831
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 165 deletions.
11 changes: 11 additions & 0 deletions python/core/processing/qgsprocessingprovider.sip.in
Expand Up @@ -204,6 +204,17 @@ Returns the matching algorithm by ``name``, or a None if no matching
algorithm is contained by this provider.

.. seealso:: :py:func:`algorithms`
%End

virtual QgsProcessingParameterDefinition *createParameter( const QString &type, const QString &name ) /Factory/;
%Docstring
Returns a new parameter from of the given type and name.
This should be reimplemented by providers that implement custom types.
If the provider does not implement the parameter with ``type``, a None will be returned.

By default, this returns a None.

.. versionadded:: 3.2
%End

signals:
Expand Down
89 changes: 46 additions & 43 deletions python/plugins/processing/algs/qgis/FieldsMapper.py
Expand Up @@ -54,49 +54,8 @@ def tags(self):
return self.tr('attributes,table').split(',')

def initParameters(self, config=None):

class ParameterFieldsMapping(QgsProcessingParameterDefinition):

def __init__(self, name, description, parentLayerParameterName='INPUT'):
super().__init__(name, description)
self._parentLayerParameter = parentLayerParameterName

def clone(self):
copy = ParameterFieldsMapping(self.name(), self.description(), self._parentLayerParameter)
return copy

def type(self):
return 'fields_mapping'

def checkValueIsAcceptable(self, value, context=None):
if not isinstance(value, list):
return False
for field_def in value:
if not isinstance(field_def, dict):
return False
if 'name' not in field_def.keys():
return False
if 'type' not in field_def.keys():
return False
if 'expression' not in field_def.keys():
return False
return True

def valueAsPythonString(self, value, context):
return str(value)

def asScriptCode(self):
raise NotImplementedError()

@classmethod
def fromScriptCode(cls, name, description, isOptional, definition):
raise NotImplementedError()

def parentLayerParameter(self):
return self._parentLayerParameter

fields_mapping = ParameterFieldsMapping(self.FIELDS_MAPPING,
description=self.tr('Fields mapping'))
fields_mapping = FieldsMapper.ParameterFieldsMapping(self.FIELDS_MAPPING,
description=self.tr('Fields mapping'))
fields_mapping.setMetadata({
'widget_wrapper': 'processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper'
})
Expand Down Expand Up @@ -181,3 +140,47 @@ def processFeature(self, feature, context, feedback):
feature.setAttributes(attributes)
self._row_number += 1
return [feature]

class ParameterFieldsMapping(QgsProcessingParameterDefinition):

def __init__(self, name, description='', parentLayerParameterName='INPUT'):
super().__init__(name, description)
self._parentLayerParameter = parentLayerParameterName

def clone(self):
copy = FieldsMapper.ParameterFieldsMapping(self.name(), self.description(), self._parentLayerParameter)
return copy

def type(self):
return self.typeName()

@staticmethod
def typeName():
return 'fields_mapping'

def checkValueIsAcceptable(self, value, context=None):
if not isinstance(value, list):
return False
for field_def in value:
if not isinstance(field_def, dict):
return False
if 'name' not in field_def.keys():
return False
if 'type' not in field_def.keys():
return False
if 'expression' not in field_def.keys():
return False
return True

def valueAsPythonString(self, value, context):
return str(value)

def asScriptCode(self):
raise NotImplementedError()

@classmethod
def fromScriptCode(cls, name, description, isOptional, definition):
raise NotImplementedError()

def parentLayerParameter(self):
return self._parentLayerParameter
27 changes: 27 additions & 0 deletions python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py
Expand Up @@ -36,6 +36,8 @@
from qgis.core import (QgsApplication,
QgsProcessingProvider)

from PyQt5.QtCore import QCoreApplication

from processing.script import ScriptUtils

from .QgisAlgorithm import QgisAlgorithm
Expand Down Expand Up @@ -154,6 +156,7 @@


class QgisAlgorithmProvider(QgsProcessingProvider):
fieldMappingParameterName = QCoreApplication.translate('Processing', 'Fields Mapper')

def __init__(self):
super().__init__()
Expand Down Expand Up @@ -317,5 +320,29 @@ def loadAlgorithms(self):
for a in self.externalAlgs:
self.addAlgorithm(a)

def load(self):
success = super().load()

from processing.core.Processing import Processing

if success:
Processing.registerParameter(
'Fields Mapper',
self.fieldMappingParameterName,
parameter=FieldsMapper.ParameterFieldsMapping,
metadata={'widget_wrapper': 'processing.algs.qgis.ui.FieldsMappingPanel.FieldsMappingWidgetWrapper'}
)

return success

def unload(self):
super().unload()
from processing.core.Processing import Processing
Processing.unregisterParameter(self.fieldMappingParameterName)

def createParameter(self, type, name):
if type == 'fields_mapping':
return FieldsMapper.ParameterFieldsMapping(name)

def supportsNonFileBasedOutput(self):
return True
51 changes: 43 additions & 8 deletions python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py
Expand Up @@ -36,6 +36,7 @@
QVariant,
Qt,
pyqtSlot,
QCoreApplication
)
from qgis.PyQt.QtWidgets import (
QComboBox,
Expand All @@ -45,6 +46,8 @@
QMessageBox,
QSpinBox,
QStyledItemDelegate,
QWidget,
QVBoxLayout
)

from qgis.core import (
Expand All @@ -58,8 +61,9 @@
)
from qgis.gui import QgsFieldExpressionWidget

from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD, DIALOG_MODELER
from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD, DIALOG_MODELER, DIALOG_BATCH
from processing.tools import dataobjects
from processing.algs.qgis.FieldsMapper import FieldsMapper


pluginPath = os.path.dirname(__file__)
Expand Down Expand Up @@ -141,7 +145,10 @@ def columnCount(self, parent=QModelIndex()):
def rowCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return self._mapping.__len__()
try:
return len(self._mapping)
except TypeError:
return 0

def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
Expand Down Expand Up @@ -473,9 +480,31 @@ def __init__(self, *args, **kwargs):
self._layer = None

def createWidget(self):
panel = FieldsMappingPanel()
panel.dialogType = self.dialogType
return panel
self.panel = FieldsMappingPanel()
self.panel.dialogType = self.dialogType

if self.dialogType == DIALOG_MODELER:
self.combobox = QComboBox()
self.combobox.addItem(QCoreApplication.translate('Processing', '[Preconfigure]'), None)
fieldsMappingInputs = self.dialog.getAvailableValuesOfType(FieldsMapper.ParameterFieldsMapping)
for input in fieldsMappingInputs:
self.combobox.addItem(self.dialog.resolveValueDescription(input), input)

def updatePanelEnabledState():
if self.combobox.currentData() is None:
self.panel.setEnabled(True)
else:
self.panel.setEnabled(False)

self.combobox.currentIndexChanged.connect(updatePanelEnabledState)

widget = QWidget()
widget.setLayout(QVBoxLayout())
widget.layout().addWidget(self.combobox)
widget.layout().addWidget(self.panel)
return widget
else:
return self.panel

def postInitialize(self, wrappers):
for wrapper in wrappers:
Expand Down Expand Up @@ -506,10 +535,16 @@ def setLayer(self, layer):
if not isinstance(layer, QgsVectorLayer):
layer = None
self._layer = layer
self.widget.setLayer(self._layer)
self.panel.setLayer(self._layer)

def setValue(self, value):
self.widget.setValue(value)
self.panel.setValue(value)

def value(self):
return self.widget.value()
if self.dialogType == DIALOG_MODELER:
if self.combobox.currentData() is None:
return self.panel.value()
else:
return self.comboValue(combobox=self.combobox)
else:
return self.panel.value()
38 changes: 38 additions & 0 deletions python/plugins/processing/core/Processing.py
Expand Up @@ -45,6 +45,8 @@
QgsProcessingOutputMapLayer,
QgsProcessingOutputMultipleLayers)

from .parameters import initializeParameters

import processing
from processing.core.ProcessingConfig import ProcessingConfig
from processing.gui.MessageBarProgress import MessageBarProgress
Expand All @@ -67,6 +69,7 @@

class Processing(object):
BASIC_PROVIDERS = []
REGISTERED_PARAMETERS = dict()

@staticmethod
def activateProvider(providerOrName, activate=True):
Expand Down Expand Up @@ -97,6 +100,7 @@ def initialize():
if QgsApplication.processingRegistry().addProvider(p):
Processing.BASIC_PROVIDERS.append(p)
# And initialize
initializeParameters()
ProcessingConfig.initialize()
ProcessingConfig.readSettings()
RenderingStyles.loadStyles()
Expand All @@ -107,6 +111,40 @@ def deinitialize():
QgsApplication.processingRegistry().removeProvider(p)

Processing.BASIC_PROVIDERS = []
Processing.REGISTERED_PARAMETERS = dict()

@staticmethod
def registerParameter(id, name, parameter, metadata=dict(), description=None, exposeToModeller=True):
"""Register a new parameter.
The ``name`` is a human readable translated string, the ``parameter`` is a class type with the base class ``qgis.core.QgsProcessingParameterDefinition``,
the ``metadata`` is a dictionary with additional metadata, mainly used for widget wrappers.
"""
Processing.REGISTERED_PARAMETERS[id] = {
'name': name,
'parameter': parameter,
'metadata': metadata,
'description': description,
'exposeToModeller': exposeToModeller
}

@staticmethod
def unregisterParameter(name):
"""Unregister a registered parameter with the given name.
"""
del Processing.REGISTERED_PARAMETERS[name]

@staticmethod
def registeredParameters():
"""Returns a dict of registered parameters. The key of the dict is the id of the parameter.
Each entry is itself a dict with the keys
- name: The human readable name of the parameter
- parameter: The class of the parameter
- metadata: Additional metadata for the parameter, mainly used for widget wrappers
- description: A longer description for the parameter, suitable for tooltips etc
- exposeToModeller: A boolean indicating if the parameter is available as model parameter input
"""
return Processing.REGISTERED_PARAMETERS

@staticmethod
def runAlgorithm(algOrName, parameters, onFinish=None, feedback=None, context=None):
Expand Down

0 comments on commit 4f4b831

Please sign in to comment.