mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Create FieldMapper GeoAlgorithm
This commit is contained in:
parent
88c928af56
commit
9e51488a88
@ -478,6 +478,7 @@
|
||||
<file>themes/default/cadtools/perpendicular.png</file>
|
||||
<file>themes/default/mIconSuccess.png</file>
|
||||
<file>themes/default/bubble.svg</file>
|
||||
<file>themes/default/mIconClear.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/images/tips">
|
||||
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
|
||||
|
137
python/plugins/processing/algs/qgis/FieldsMapper.py
Normal file
137
python/plugins/processing/algs/qgis/FieldsMapper.py
Normal file
@ -0,0 +1,137 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
FieldsMapper.py
|
||||
---------------------
|
||||
Date : October 2014
|
||||
Copyright : (C) 2014 by Arnaud Morvan
|
||||
Email : arnaud dot morvan at camptocamp dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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__ = 'Arnaud Morvan'
|
||||
__date__ = 'October 2014'
|
||||
__copyright__ = '(C) 2014, Arnaud Morvan'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
from qgis.core import QgsField, QgsExpression, QgsFeature
|
||||
from processing.core.GeoAlgorithm import GeoAlgorithm
|
||||
from processing.core.GeoAlgorithmExecutionException import \
|
||||
GeoAlgorithmExecutionException
|
||||
from processing.core.parameters import ParameterVector
|
||||
from processing.core.outputs import OutputVector
|
||||
from processing.tools import dataobjects, vector
|
||||
|
||||
from .fieldsmapping import ParameterFieldsMapping
|
||||
from .ui.FieldsMapperDialogs import (FieldsMapperParametersDialog,
|
||||
FieldsMapperModelerParametersDialog)
|
||||
|
||||
class FieldsMapper(GeoAlgorithm):
|
||||
|
||||
INPUT_LAYER = 'INPUT_LAYER'
|
||||
FIELDS_MAPPING = 'FIELDS_MAPPING'
|
||||
OUTPUT_LAYER = 'OUTPUT_LAYER'
|
||||
|
||||
def __init__(self):
|
||||
GeoAlgorithm.__init__(self)
|
||||
self.mapping = None
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.name = 'Refactor fields'
|
||||
self.group = 'Vector table tools'
|
||||
self.addParameter(ParameterVector(self.INPUT_LAYER,
|
||||
self.tr('Input layer'),
|
||||
[ParameterVector.VECTOR_TYPE_ANY], False))
|
||||
self.addParameter(ParameterFieldsMapping(self.FIELDS_MAPPING,
|
||||
self.tr('Fields mapping'), self.INPUT_LAYER))
|
||||
self.addOutput(OutputVector(self.OUTPUT_LAYER,
|
||||
self.tr('Output layer')))
|
||||
|
||||
def getCustomParametersDialog(self):
|
||||
return FieldsMapperParametersDialog(self)
|
||||
|
||||
def getCustomModelerParametersDialog(self, modelAlg, algIndex=None):
|
||||
return FieldsMapperModelerParametersDialog(self, modelAlg, algIndex)
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
layer = self.getParameterValue(self.INPUT_LAYER)
|
||||
mapping = self.getParameterValue(self.FIELDS_MAPPING)
|
||||
output = self.getOutputFromName(self.OUTPUT_LAYER)
|
||||
|
||||
layer = dataobjects.getObjectFromUri(layer)
|
||||
provider = layer.dataProvider()
|
||||
fields = []
|
||||
expressions = []
|
||||
for field_def in mapping:
|
||||
fields.append(QgsField(name=field_def['name'],
|
||||
type=field_def['type'],
|
||||
len=field_def['length'],
|
||||
prec=field_def['precision']))
|
||||
|
||||
expression = QgsExpression(field_def['expression'])
|
||||
if expression.hasParserError():
|
||||
raise GeoAlgorithmExecutionException(
|
||||
self.tr(u'Parser error in expression "{}": {}')
|
||||
.format(unicode(field_def['expression']),
|
||||
unicode(expression.parserErrorString())))
|
||||
expression.prepare(provider.fields())
|
||||
if expression.hasEvalError():
|
||||
raise GeoAlgorithmExecutionException(
|
||||
self.tr(u'Evaluation error in expression "{}": {}')
|
||||
.format(unicode(field_def['expression']),
|
||||
unicode(expression.evalErrorString())))
|
||||
expressions.append(expression)
|
||||
|
||||
writer = output.getVectorWriter(fields,
|
||||
provider.geometryType(),
|
||||
layer.crs())
|
||||
|
||||
# Create output vector layer with new attributes
|
||||
error = ''
|
||||
calculationSuccess = True
|
||||
inFeat = QgsFeature()
|
||||
outFeat = QgsFeature()
|
||||
features = vector.features(layer)
|
||||
count = len(features)
|
||||
for current, inFeat in enumerate(features):
|
||||
rownum = current + 1
|
||||
|
||||
outFeat.setGeometry(inFeat.geometry())
|
||||
|
||||
attrs = []
|
||||
for i in xrange(0, len(mapping)):
|
||||
field_def = mapping[i]
|
||||
expression = expressions[i]
|
||||
expression.setCurrentRowNumber(rownum)
|
||||
value = expression.evaluate(inFeat)
|
||||
if expression.hasEvalError():
|
||||
calculationSuccess = False
|
||||
error = expression.evalErrorString()
|
||||
break
|
||||
|
||||
attrs.append(value)
|
||||
outFeat.setAttributes(attrs)
|
||||
|
||||
writer.addFeature(outFeat)
|
||||
|
||||
current += 1
|
||||
progress.setPercentage(100 * current / float(count))
|
||||
|
||||
del writer
|
||||
|
||||
if not calculationSuccess:
|
||||
raise GeoAlgorithmExecutionException(
|
||||
self.tr('An error occurred while evaluating the calculation'
|
||||
' string:\n') + error)
|
@ -125,6 +125,7 @@ from SetRasterStyle import SetRasterStyle
|
||||
from SelectByExpression import SelectByExpression
|
||||
from HypsometricCurves import HypsometricCurves
|
||||
from SplitLinesWithLines import SplitLinesWithLines
|
||||
from processing.algs.qgis.FieldsMapper import FieldsMapper
|
||||
|
||||
import processing.resources_rc
|
||||
|
||||
@ -171,6 +172,7 @@ class QGISAlgorithmProvider(AlgorithmProvider):
|
||||
SetVectorStyle(), SetRasterStyle(),
|
||||
SelectByExpression(), HypsometricCurves(),
|
||||
SplitLinesWithLines(), CreateConstantRaster(),
|
||||
FieldsMapper(),
|
||||
]
|
||||
|
||||
if hasMatplotlib:
|
||||
|
55
python/plugins/processing/algs/qgis/fieldsmapping.py
Normal file
55
python/plugins/processing/algs/qgis/fieldsmapping.py
Normal file
@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
FieldsMapper.py
|
||||
---------------------
|
||||
Date : October 2014
|
||||
Copyright : (C) 2014 by Arnaud Morvan
|
||||
Email : arnaud dot morvan at camptocamp dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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__ = 'Arnaud Morvan'
|
||||
__date__ = 'October 2014'
|
||||
__copyright__ = '(C) 2014, Arnaud Morvan'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
from processing.core.parameters import Parameter
|
||||
|
||||
|
||||
class ParameterFieldsMapping(Parameter):
|
||||
|
||||
def __init__(self, name='', description='', parent=None):
|
||||
Parameter.__init__(self, name, description)
|
||||
self.parent = parent
|
||||
self.value = []
|
||||
|
||||
def getValueAsCommandLineParameter(self):
|
||||
return '"' + unicode(self.value) + '"'
|
||||
|
||||
def setValue(self, value):
|
||||
if value is None:
|
||||
return False
|
||||
if isinstance(value, list):
|
||||
self.value = value
|
||||
return True
|
||||
if isinstance(value, unicode):
|
||||
try:
|
||||
self.value = eval(value)
|
||||
return True
|
||||
except Exception as e:
|
||||
print unicode(e) # display error in console
|
||||
return False
|
||||
return False
|
140
python/plugins/processing/algs/qgis/ui/FieldsMapperDialogs.py
Normal file
140
python/plugins/processing/algs/qgis/ui/FieldsMapperDialogs.py
Normal file
@ -0,0 +1,140 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
FieldsMapper.py
|
||||
---------------------
|
||||
Date : October 2014
|
||||
Copyright : (C) 2014 by Arnaud Morvan
|
||||
Email : arnaud dot morvan at camptocamp dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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__ = 'Arnaud Morvan'
|
||||
__date__ = 'October 2014'
|
||||
__copyright__ = '(C) 2014, Arnaud Morvan'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
from PyQt4.QtGui import QComboBox, QSpacerItem
|
||||
|
||||
from processing.core.parameters import ParameterVector
|
||||
from processing.tools import dataobjects
|
||||
from processing.gui.ParametersPanel import ParametersPanel
|
||||
from processing.gui.AlgorithmDialog import AlgorithmDialog, AlgorithmDialogBase
|
||||
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
|
||||
|
||||
from processing.algs.qgis.fieldsmapping import ParameterFieldsMapping
|
||||
from .FieldsMappingPanel import FieldsMappingPanel
|
||||
|
||||
|
||||
class FieldsMapperParametersPanel(ParametersPanel):
|
||||
|
||||
def __init__(self, parent, alg):
|
||||
ParametersPanel.__init__(self, parent, alg)
|
||||
|
||||
item = self.layoutMain.itemAt(self.layoutMain.count() - 1)
|
||||
if isinstance(item, QSpacerItem):
|
||||
self.layoutMain.removeItem(item)
|
||||
item = None
|
||||
|
||||
def getWidgetFromParameter(self, param):
|
||||
if isinstance(param, ParameterFieldsMapping):
|
||||
item = FieldsMappingPanel()
|
||||
if param.parent in self.dependentItems:
|
||||
items = self.dependentItems[param.parent]
|
||||
else:
|
||||
items = []
|
||||
self.dependentItems[param.parent] = items
|
||||
items.append(param.name)
|
||||
parent = self.alg.getParameterFromName(param.parent)
|
||||
if isinstance(parent, ParameterVector):
|
||||
layers = dataobjects.getVectorLayers(parent.shapetype)
|
||||
else:
|
||||
layers = dataobjects.getTables()
|
||||
if len(layers) > 0:
|
||||
item.setLayer(layers[0])
|
||||
return item
|
||||
return ParametersPanel.getWidgetFromParameter(self, param)
|
||||
|
||||
def updateDependentFields(self):
|
||||
sender = self.sender()
|
||||
if not isinstance(sender, QComboBox):
|
||||
return
|
||||
if not sender.name in self.dependentItems:
|
||||
return
|
||||
layer = sender.itemData(sender.currentIndex())
|
||||
children = self.dependentItems[sender.name]
|
||||
for child in children:
|
||||
widget = self.valueItems[child]
|
||||
if isinstance(widget, FieldsMappingPanel):
|
||||
widget.setLayer(layer)
|
||||
|
||||
def somethingDependsOnThisParameter(self, parent):
|
||||
for param in self.alg.parameters:
|
||||
if isinstance(param, ParameterFieldsMapping):
|
||||
if param.parent == parent.name:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class FieldsMapperParametersDialog(AlgorithmDialog):
|
||||
def __init__(self, alg):
|
||||
AlgorithmDialogBase.__init__(self, alg)
|
||||
|
||||
self.alg = alg
|
||||
|
||||
self.mainWidget = FieldsMapperParametersPanel(self, alg)
|
||||
self.setMainWidget()
|
||||
|
||||
def setParamValue(self, param, widget, alg=None):
|
||||
if isinstance(param, ParameterFieldsMapping):
|
||||
return param.setValue(widget.value())
|
||||
return AlgorithmDialog.setParamValue(self, param, widget, alg)
|
||||
|
||||
|
||||
class FieldsMapperModelerParametersDialog(ModelerParametersDialog):
|
||||
|
||||
def __init__(self, alg, model, algName=None):
|
||||
ModelerParametersDialog.__init__(self, alg, model, algName)
|
||||
|
||||
paramsLayout = self.paramPanel.layout()
|
||||
item = paramsLayout.itemAt(paramsLayout.count() - 1)
|
||||
if isinstance(item, QSpacerItem):
|
||||
paramsLayout.removeItem(item)
|
||||
item = None
|
||||
|
||||
def getWidgetFromParameter(self, param):
|
||||
if isinstance(param, ParameterFieldsMapping):
|
||||
return FieldsMappingPanel()
|
||||
return ModelerParametersDialog.getWidgetFromParameter(self, param)
|
||||
|
||||
def setPreviousValues(self):
|
||||
ModelerParametersDialog.setPreviousValues(self)
|
||||
if self._algName is not None:
|
||||
alg = self.model.algs[self._algName]
|
||||
for param in alg.algorithm.parameters:
|
||||
if isinstance(param, ParameterFieldsMapping):
|
||||
widget = self.valueItems[param.name]
|
||||
value = alg.params[param.name]
|
||||
if isinstance(value, unicode):
|
||||
# convert to list because of ModelerAlgorithme.resolveValue behavior with lists
|
||||
value = eval(value)
|
||||
widget.setValue(value)
|
||||
|
||||
def setParamValue(self, alg, param, widget):
|
||||
if isinstance(param, ParameterFieldsMapping):
|
||||
# convert to unicode because of ModelerAlgorithme.resolveValue behavior with lists
|
||||
alg.params[param.name] = unicode(widget.value())
|
||||
return True
|
||||
return ModelerParametersDialog.setParamValue(self, alg, param, widget)
|
466
python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py
Normal file
466
python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py
Normal file
@ -0,0 +1,466 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
FieldsMappingWidget.py
|
||||
---------------------
|
||||
Date : October 2014
|
||||
Copyright : (C) 2014 by Arnaud Morvan
|
||||
Email : arnaud dot morvan at camptocamp dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* 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__ = 'Arnaud Morvan'
|
||||
__date__ = 'October 2014'
|
||||
__copyright__ = '(C) 2014, Arnaud Morvan'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from qgis.core import QgsExpression
|
||||
from qgis.gui import QgsFieldExpressionWidget
|
||||
|
||||
from processing.tools import dataobjects
|
||||
|
||||
from .ui_widgetFieldsMapping import Ui_Form
|
||||
|
||||
|
||||
class FieldsMappingModel(QtCore.QAbstractTableModel):
|
||||
|
||||
fieldTypes = OrderedDict([
|
||||
(QtCore.QVariant.Int, "Integer"),
|
||||
(QtCore.QVariant.Double, "Double"),
|
||||
(QtCore.QVariant.String, "String"),
|
||||
(QtCore.QVariant.Date, "Date")])
|
||||
|
||||
columns = [
|
||||
{'name': 'name', 'type': QtCore.QVariant.String},
|
||||
{'name': 'type', 'type': QtCore.QVariant.Type},
|
||||
{'name': 'length', 'type': QtCore.QVariant.Int},
|
||||
{'name': 'precision', 'type': QtCore.QVariant.Int},
|
||||
# {'name': 'comment', 'type': QtCore.QVariant.String},
|
||||
{'name': 'expression', 'type': QgsExpression}]
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(FieldsMappingModel, self).__init__(parent)
|
||||
self._mapping = []
|
||||
self._errors = []
|
||||
self._layer = None
|
||||
|
||||
def mapping(self):
|
||||
return self._mapping
|
||||
|
||||
def setMapping(self, value):
|
||||
self.beginResetModel()
|
||||
self._mapping = value
|
||||
self.testAllExpressions()
|
||||
self.endResetModel()
|
||||
|
||||
def testAllExpressions(self):
|
||||
self._errors = [None for i in xrange(0, len(self._mapping))]
|
||||
for row in xrange(0, len(self._mapping)):
|
||||
self.testExpression(row)
|
||||
|
||||
def testExpression(self, row):
|
||||
self._errors[row] = None
|
||||
field = self._mapping[row]
|
||||
expression = QgsExpression(field['expression'])
|
||||
if expression.hasParserError():
|
||||
self._errors[row] = expression.parserErrorString()
|
||||
return
|
||||
if self._layer is None:
|
||||
return
|
||||
dp = self._layer.dataProvider()
|
||||
for feature in dp.getFeatures():
|
||||
expression.evaluate(feature, dp.fields())
|
||||
if expression.hasEvalError():
|
||||
self._errors[row] = expression.evalErrorString()
|
||||
return
|
||||
break
|
||||
|
||||
def layer(self):
|
||||
return self._layer
|
||||
|
||||
def setLayer(self, layer):
|
||||
self._layer = layer
|
||||
self.testAllExpressions()
|
||||
|
||||
def columnCount(self, parent=QtCore.QModelIndex()):
|
||||
if parent.isValid():
|
||||
return 0
|
||||
return len(self.columns)
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
if parent.isValid():
|
||||
return 0
|
||||
return self._mapping.__len__()
|
||||
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
return self.columns[section]['name'].title()
|
||||
if orientation == QtCore.Qt.Vertical:
|
||||
return section
|
||||
|
||||
def flags(self, index):
|
||||
flags = (QtCore.Qt.ItemIsSelectable
|
||||
+ QtCore.Qt.ItemIsEditable
|
||||
+ QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
return QtCore.Qt.ItemFlags(flags)
|
||||
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
column = index.column()
|
||||
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
field = self._mapping[index.row()]
|
||||
column_def = self.columns[column]
|
||||
value = field[column_def['name']]
|
||||
|
||||
fieldType = column_def['type']
|
||||
if fieldType == QtCore.QVariant.Type:
|
||||
if value == 0:
|
||||
return ''
|
||||
return self.fieldTypes[value]
|
||||
return value
|
||||
|
||||
if role == QtCore.Qt.EditRole:
|
||||
field = self._mapping[index.row()]
|
||||
column_def = self.columns[column]
|
||||
value = field[column_def['name']]
|
||||
return value
|
||||
|
||||
if role == QtCore.Qt.TextAlignmentRole:
|
||||
fieldType = self.columns[column]['type']
|
||||
if fieldType in [QtCore.QVariant.Int]:
|
||||
hAlign = QtCore.Qt.AlignRight
|
||||
else:
|
||||
hAlign = QtCore.Qt.AlignLeft
|
||||
return hAlign + QtCore.Qt.AlignVCenter
|
||||
|
||||
if role == QtCore.Qt.ForegroundRole:
|
||||
column_def = self.columns[column]
|
||||
if column_def['name'] == 'expression':
|
||||
brush = QtGui.QBrush()
|
||||
if self._errors[index.row()]:
|
||||
brush.setColor(QtCore.Qt.red)
|
||||
else:
|
||||
brush.setColor(QtCore.Qt.black)
|
||||
return brush
|
||||
|
||||
if role == QtCore.Qt.ToolTipRole:
|
||||
column_def = self.columns[column]
|
||||
if column_def['name'] == 'expression':
|
||||
return self._errors[index.row()]
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
if role == QtCore.Qt.EditRole:
|
||||
field = self._mapping[index.row()]
|
||||
column = index.column()
|
||||
column_def = self.columns[column]
|
||||
field[column_def['name']] = value
|
||||
if column_def['name'] == 'expression':
|
||||
self.testExpression(index.row())
|
||||
self.dataChanged.emit(index, index)
|
||||
return True
|
||||
|
||||
def insertRows(self, row, count, index=QtCore.QModelIndex()):
|
||||
self.beginInsertRows(index, row, row + count - 1)
|
||||
|
||||
for i in xrange(0, count):
|
||||
field = self.newField()
|
||||
self._mapping.insert(row + i, field)
|
||||
self._errors.insert(row + i, None)
|
||||
self.testExpression(row)
|
||||
|
||||
self.endInsertRows()
|
||||
return True
|
||||
|
||||
def removeRows(self, row, count, index=QtCore.QModelIndex()):
|
||||
self.beginRemoveRows(index, row, row + count - 1)
|
||||
|
||||
for i in xrange(row + count - 1, row + 1):
|
||||
self._mapping.pop(i)
|
||||
self._errors.pop(i)
|
||||
|
||||
self.endRemoveRows()
|
||||
return True
|
||||
|
||||
def newField(self, field=None):
|
||||
if field is None:
|
||||
return {'name': '',
|
||||
'type': QtCore.QVariant.Invalid,
|
||||
'length': 0,
|
||||
'precision': 0,
|
||||
'expression': ''}
|
||||
|
||||
return {'name': field.name(),
|
||||
'type': field.type(),
|
||||
'length': field.length(),
|
||||
'precision': field.precision(),
|
||||
'expression': field.name()}
|
||||
|
||||
def loadLayerFields(self, layer):
|
||||
self.beginResetModel()
|
||||
|
||||
self._mapping = []
|
||||
if layer is not None:
|
||||
dp = layer.dataProvider()
|
||||
for field in dp.fields():
|
||||
self._mapping.append(self.newField(field))
|
||||
self.testAllExpressions()
|
||||
|
||||
self.endResetModel()
|
||||
|
||||
|
||||
class FieldDelegate(QtGui.QStyledItemDelegate):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(FieldDelegate, self).__init__(parent)
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
column = index.column()
|
||||
|
||||
fieldType = FieldsMappingModel.columns[column]['type']
|
||||
if fieldType == QtCore.QVariant.Type:
|
||||
editor = QtGui.QComboBox(parent)
|
||||
for key, text in FieldsMappingModel.fieldTypes.iteritems():
|
||||
editor.addItem(text, key)
|
||||
|
||||
elif fieldType == QgsExpression:
|
||||
editor = QgsFieldExpressionWidget(parent)
|
||||
editor.setLayer(index.model().layer())
|
||||
# editor.fieldChanged.connect(self.on_expression_fieldChange)
|
||||
|
||||
else:
|
||||
editor = QtGui.QStyledItemDelegate.createEditor(self, parent, option, index)
|
||||
|
||||
editor.setAutoFillBackground(True)
|
||||
return editor
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
if not editor:
|
||||
return
|
||||
|
||||
column = index.column()
|
||||
value = index.model().data(index, QtCore.Qt.EditRole)
|
||||
|
||||
fieldType = FieldsMappingModel.columns[column]['type']
|
||||
if fieldType == QtCore.QVariant.Type:
|
||||
editor.setCurrentIndex(editor.findData(value))
|
||||
|
||||
elif fieldType == QgsExpression:
|
||||
editor.setField(value)
|
||||
|
||||
else:
|
||||
QtGui.QStyledItemDelegate.setEditorData(self, editor, index)
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
if not editor:
|
||||
return
|
||||
|
||||
column = index.column()
|
||||
|
||||
fieldType = FieldsMappingModel.columns[column]['type']
|
||||
if fieldType == QtCore.QVariant.Type:
|
||||
value = editor.itemData(editor.currentIndex())
|
||||
model.setData(index, value)
|
||||
|
||||
elif fieldType == QgsExpression:
|
||||
(value, isExpression, isValid) = editor.currentField()
|
||||
model.setData(index, value)
|
||||
|
||||
else:
|
||||
QtGui.QStyledItemDelegate.setModelData(self, editor, model, index)
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
editor.setGeometry(option.rect)
|
||||
|
||||
def on_expression_fieldChange(self, fieldName, isValid):
|
||||
# self.commitData.emit(self.sender())
|
||||
pass
|
||||
|
||||
|
||||
class FieldsMappingPanel(QtGui.QWidget, Ui_Form):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.addButton.setIcon(
|
||||
QtGui.QIcon(':/images/themes/default/mActionAdd.svg'))
|
||||
self.deleteButton.setIcon(
|
||||
QtGui.QIcon(':/images/themes/default/mActionRemove.svg'))
|
||||
self.upButton.setIcon(
|
||||
QtGui.QIcon(':/images/themes/default/mActionArrowUp.png'))
|
||||
self.downButton.setIcon(
|
||||
QtGui.QIcon(':/images/themes/default/mActionArrowDown.png'))
|
||||
self.resetButton.setIcon(
|
||||
QtGui.QIcon(':/images/themes/default/mIconClear.png'))
|
||||
|
||||
self.model = FieldsMappingModel()
|
||||
self.fieldsView.setModel(self.model)
|
||||
|
||||
self.model.rowsInserted.connect(self.on_model_rowsInserted)
|
||||
self.fieldsView.setItemDelegate(FieldDelegate())
|
||||
|
||||
self.updateLayerCombo()
|
||||
|
||||
def setLayer(self, layer):
|
||||
self.model.setLayer(layer)
|
||||
if self.model.rowCount() == 0:
|
||||
self.on_resetButton_clicked()
|
||||
else:
|
||||
dlg = QtGui.QMessageBox(self)
|
||||
dlg.setText("Do you want to reset the field mapping?")
|
||||
dlg.setStandardButtons(
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes
|
||||
+ QtGui.QMessageBox.No))
|
||||
dlg.setDefaultButton(QtGui.QMessageBox.No)
|
||||
if dlg.exec_() == QtGui.QMessageBox.Yes:
|
||||
self.on_resetButton_clicked()
|
||||
|
||||
def value(self):
|
||||
return self.model.mapping()
|
||||
|
||||
def setValue(self, value):
|
||||
self.model.setMapping(value)
|
||||
|
||||
@QtCore.pyqtSlot(bool, name='on_addButton_clicked')
|
||||
def on_addButton_clicked(self, checked=False):
|
||||
rowCount = self.model.rowCount()
|
||||
self.model.insertRows(rowCount, 1)
|
||||
index = self.model.index(rowCount, 0)
|
||||
self.fieldsView.selectionModel().select(index,
|
||||
QtGui.QItemSelectionModel.SelectionFlags(QtGui.QItemSelectionModel.Clear
|
||||
+ QtGui.QItemSelectionModel.Select
|
||||
+ QtGui.QItemSelectionModel.Current
|
||||
+ QtGui.QItemSelectionModel.Rows))
|
||||
self.fieldsView.scrollTo(index)
|
||||
self.fieldsView.scrollTo(index)
|
||||
|
||||
@QtCore.pyqtSlot(bool, name='on_deleteButton_clicked')
|
||||
def on_deleteButton_clicked(self, checked=False):
|
||||
sel = self.fieldsView.selectionModel()
|
||||
if not sel.hasSelection():
|
||||
return
|
||||
|
||||
indexes = sel.selectedRows()
|
||||
for index in indexes:
|
||||
self.model.removeRows(index.row(), 1)
|
||||
|
||||
@QtCore.pyqtSlot(bool, name='on_upButton_clicked')
|
||||
def on_upButton_clicked(self, checked=False):
|
||||
sel = self.fieldsView.selectionModel()
|
||||
if not sel.hasSelection():
|
||||
return
|
||||
|
||||
row = sel.selectedRows()[0].row()
|
||||
if row == 0:
|
||||
return
|
||||
|
||||
self.model.insertRows(row - 1, 1)
|
||||
|
||||
for column in xrange(0, self.model.columnCount()):
|
||||
srcIndex = self.model.index(row + 1, column)
|
||||
dstIndex = self.model.index(row - 1, column)
|
||||
value = self.model.data(srcIndex, QtCore.Qt.EditRole)
|
||||
self.model.setData(dstIndex, value, QtCore.Qt.EditRole)
|
||||
|
||||
self.model.removeRows(row + 1, 1)
|
||||
|
||||
sel.select(self.model.index(row - 1, 0),
|
||||
QtGui.QItemSelectionModel.SelectionFlags(QtGui.QItemSelectionModel.Clear
|
||||
+ QtGui.QItemSelectionModel.Select
|
||||
+ QtGui.QItemSelectionModel.Current
|
||||
+ QtGui.QItemSelectionModel.Rows))
|
||||
|
||||
@QtCore.pyqtSlot(bool, name='on_downButton_clicked')
|
||||
def on_downButton_clicked(self, checked=False):
|
||||
sel = self.fieldsView.selectionModel()
|
||||
if not sel.hasSelection():
|
||||
return
|
||||
|
||||
row = sel.selectedRows()[0].row()
|
||||
if row == self.model.rowCount() - 1:
|
||||
return
|
||||
|
||||
self.model.insertRows(row + 2, 1)
|
||||
|
||||
for column in xrange(0, self.model.columnCount()):
|
||||
srcIndex = self.model.index(row, column)
|
||||
dstIndex = self.model.index(row + 2, column)
|
||||
value = self.model.data(srcIndex, QtCore.Qt.EditRole)
|
||||
self.model.setData(dstIndex, value, QtCore.Qt.EditRole)
|
||||
|
||||
self.model.removeRows(row, 1)
|
||||
|
||||
sel.select(self.model.index(row + 1, 0),
|
||||
QtGui.QItemSelectionModel.SelectionFlags(QtGui.QItemSelectionModel.Clear
|
||||
+ QtGui.QItemSelectionModel.Select
|
||||
+ QtGui.QItemSelectionModel.Current
|
||||
+ QtGui.QItemSelectionModel.Rows))
|
||||
|
||||
@QtCore.pyqtSlot(bool, name='on_resetButton_clicked')
|
||||
def on_resetButton_clicked(self, checked=False):
|
||||
self.model.loadLayerFields(self.model.layer())
|
||||
self.openPersistentEditor(
|
||||
self.model.index(0, 0),
|
||||
self.model.index(self.model.rowCount() - 1,
|
||||
self.model.columnCount() - 1))
|
||||
self.resizeColumns()
|
||||
|
||||
def resizeColumns(self):
|
||||
header = self.fieldsView.horizontalHeader()
|
||||
header.resizeSections(QtGui.QHeaderView.ResizeToContents)
|
||||
for section in xrange(0, header.count()):
|
||||
size = header.sectionSize(section)
|
||||
fieldType = FieldsMappingModel.columns[section]['type']
|
||||
if fieldType == QgsExpression:
|
||||
header.resizeSection(section, size + 100)
|
||||
else:
|
||||
header.resizeSection(section, size + 20)
|
||||
|
||||
def openPersistentEditor(self, topLeft, bottomRight):
|
||||
return
|
||||
for row in xrange(topLeft.row(), bottomRight.row() + 1):
|
||||
for column in xrange(topLeft.column(), bottomRight.column() + 1):
|
||||
self.fieldsView.openPersistentEditor(self.model.index(row, column))
|
||||
editor = self.fieldsView.indexWidget(self.model.index(row, column))
|
||||
if isinstance(editor, QtGui.QLineEdit):
|
||||
editor.deselect()
|
||||
if isinstance(editor, QtGui.QSpinBox):
|
||||
lineEdit = editor.findChild(QtGui.QLineEdit)
|
||||
lineEdit.setAlignment(QtCore.Qt.AlignRight or QtCore.Qt.AlignVCenter)
|
||||
lineEdit.deselect()
|
||||
|
||||
def on_model_rowsInserted(self, parent, start, end):
|
||||
self.openPersistentEditor(
|
||||
self.model.index(start, 0),
|
||||
self.model.index(end, self.model.columnCount() - 1))
|
||||
|
||||
def updateLayerCombo(self):
|
||||
layers = dataobjects.getVectorLayers()
|
||||
layers.sort(key = lambda lay: lay.name())
|
||||
for layer in layers:
|
||||
self.layerCombo.addItem(layer.name(), layer)
|
||||
|
||||
@QtCore.pyqtSlot(bool, name='on_loadLayerFieldsButton_clicked')
|
||||
def on_loadLayerFieldsButton_clicked(self, checked=False):
|
||||
layer = self.layerCombo.itemData(self.layerCombo.currentIndex())
|
||||
if layer is None:
|
||||
return
|
||||
self.model.loadLayerFields(layer)
|
107
python/plugins/processing/algs/qgis/ui/ui_widgetFieldsMapping.py
Normal file
107
python/plugins/processing/algs/qgis/ui/ui_widgetFieldsMapping.py
Normal file
@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'widgetFieldsMapping.ui'
|
||||
#
|
||||
# Created: Tue Jan 20 10:14:41 2015
|
||||
# by: PyQt4 UI code generator 4.10.4
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
try:
|
||||
_encoding = QtGui.QApplication.UnicodeUTF8
|
||||
def _translate(context, text, disambig):
|
||||
return QtGui.QApplication.translate(context, text, disambig, _encoding)
|
||||
except AttributeError:
|
||||
def _translate(context, text, disambig):
|
||||
return QtGui.QApplication.translate(context, text, disambig)
|
||||
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName(_fromUtf8("Form"))
|
||||
Form.resize(590, 552)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
|
||||
Form.setSizePolicy(sizePolicy)
|
||||
self.verticalLayout_2 = QtGui.QVBoxLayout(Form)
|
||||
self.verticalLayout_2.setMargin(0)
|
||||
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
|
||||
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
|
||||
self.fieldsView = QtGui.QTableView(Form)
|
||||
self.fieldsView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
|
||||
self.fieldsView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||
self.fieldsView.setObjectName(_fromUtf8("fieldsView"))
|
||||
self.horizontalLayout.addWidget(self.fieldsView)
|
||||
self.buttonLayout = QtGui.QVBoxLayout()
|
||||
self.buttonLayout.setObjectName(_fromUtf8("buttonLayout"))
|
||||
self.addButton = QtGui.QToolButton(Form)
|
||||
self.addButton.setObjectName(_fromUtf8("addButton"))
|
||||
self.buttonLayout.addWidget(self.addButton)
|
||||
self.deleteButton = QtGui.QToolButton(Form)
|
||||
self.deleteButton.setObjectName(_fromUtf8("deleteButton"))
|
||||
self.buttonLayout.addWidget(self.deleteButton)
|
||||
self.upButton = QtGui.QToolButton(Form)
|
||||
self.upButton.setObjectName(_fromUtf8("upButton"))
|
||||
self.buttonLayout.addWidget(self.upButton)
|
||||
self.downButton = QtGui.QToolButton(Form)
|
||||
self.downButton.setObjectName(_fromUtf8("downButton"))
|
||||
self.buttonLayout.addWidget(self.downButton)
|
||||
self.resetButton = QtGui.QToolButton(Form)
|
||||
self.resetButton.setObjectName(_fromUtf8("resetButton"))
|
||||
self.buttonLayout.addWidget(self.resetButton)
|
||||
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||
self.buttonLayout.addItem(spacerItem)
|
||||
self.horizontalLayout.addLayout(self.buttonLayout)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayout)
|
||||
self.loadFromLayerLayout = QtGui.QHBoxLayout()
|
||||
self.loadFromLayerLayout.setObjectName(_fromUtf8("loadFromLayerLayout"))
|
||||
self.loadFromLayerLabel = QtGui.QLabel(Form)
|
||||
self.loadFromLayerLabel.setObjectName(_fromUtf8("loadFromLayerLabel"))
|
||||
self.loadFromLayerLayout.addWidget(self.loadFromLayerLabel)
|
||||
self.layerCombo = QtGui.QComboBox(Form)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.layerCombo.sizePolicy().hasHeightForWidth())
|
||||
self.layerCombo.setSizePolicy(sizePolicy)
|
||||
self.layerCombo.setObjectName(_fromUtf8("layerCombo"))
|
||||
self.loadFromLayerLayout.addWidget(self.layerCombo)
|
||||
self.loadLayerFieldsButton = QtGui.QPushButton(Form)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.loadLayerFieldsButton.sizePolicy().hasHeightForWidth())
|
||||
self.loadLayerFieldsButton.setSizePolicy(sizePolicy)
|
||||
self.loadLayerFieldsButton.setObjectName(_fromUtf8("loadLayerFieldsButton"))
|
||||
self.loadFromLayerLayout.addWidget(self.loadLayerFieldsButton)
|
||||
self.verticalLayout_2.addLayout(self.loadFromLayerLayout)
|
||||
|
||||
self.retranslateUi(Form)
|
||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||
|
||||
def retranslateUi(self, Form):
|
||||
Form.setWindowTitle(_translate("Form", "Fields", None))
|
||||
self.addButton.setToolTip(_translate("Form", "Add new field", None))
|
||||
self.addButton.setText(_translate("Form", "add", None))
|
||||
self.deleteButton.setToolTip(_translate("Form", "Delete selected field", None))
|
||||
self.deleteButton.setText(_translate("Form", "delete", None))
|
||||
self.upButton.setToolTip(_translate("Form", "Move selected field up", None))
|
||||
self.upButton.setText(_translate("Form", "up", None))
|
||||
self.downButton.setToolTip(_translate("Form", "Move selected field down", None))
|
||||
self.downButton.setText(_translate("Form", "down", None))
|
||||
self.resetButton.setToolTip(_translate("Form", "Reset all fields", None))
|
||||
self.resetButton.setText(_translate("Form", "reset", None))
|
||||
self.loadFromLayerLabel.setText(_translate("Form", "Load fields from layer", None))
|
||||
self.loadLayerFieldsButton.setToolTip(_translate("Form", "Load fields from selected layer", None))
|
||||
self.loadLayerFieldsButton.setText(_translate("Form", "Load fields", None))
|
||||
|
148
python/plugins/processing/algs/qgis/ui/widgetFieldsMapping.ui
Normal file
148
python/plugins/processing/algs/qgis/ui/widgetFieldsMapping.ui
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>590</width>
|
||||
<height>552</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Fields</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="fieldsView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="buttonLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="addButton">
|
||||
<property name="toolTip">
|
||||
<string>Add new field</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="deleteButton">
|
||||
<property name="toolTip">
|
||||
<string>Delete selected field</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="upButton">
|
||||
<property name="toolTip">
|
||||
<string>Move selected field up</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="downButton">
|
||||
<property name="toolTip">
|
||||
<string>Move selected field down</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="resetButton">
|
||||
<property name="toolTip">
|
||||
<string>Reset all fields</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="loadFromLayerLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="loadFromLayerLabel">
|
||||
<property name="text">
|
||||
<string>Load fields from layer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="layerCombo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="loadLayerFieldsButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Load fields from selected layer</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load fields</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user