Skip to content

Commit c4a6adc

Browse files
committedFeb 8, 2015
Merge pull request #1753 from arnaud-morvan/processing_managefields
Create FieldMapper GeoAlgorithm
2 parents afd21cd + 9e51488 commit c4a6adc

File tree

8 files changed

+1056
-0
lines changed

8 files changed

+1056
-0
lines changed
 

‎images/images.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@
478478
<file>themes/default/cadtools/perpendicular.png</file>
479479
<file>themes/default/mIconSuccess.png</file>
480480
<file>themes/default/bubble.svg</file>
481+
<file>themes/default/mIconClear.png</file>
481482
</qresource>
482483
<qresource prefix="/images/tips">
483484
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
FieldsMapper.py
6+
---------------------
7+
Date : October 2014
8+
Copyright : (C) 2014 by Arnaud Morvan
9+
Email : arnaud dot morvan at camptocamp dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Arnaud Morvan'
21+
__date__ = 'October 2014'
22+
__copyright__ = '(C) 2014, Arnaud Morvan'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
29+
from qgis.core import QgsField, QgsExpression, QgsFeature
30+
from processing.core.GeoAlgorithm import GeoAlgorithm
31+
from processing.core.GeoAlgorithmExecutionException import \
32+
GeoAlgorithmExecutionException
33+
from processing.core.parameters import ParameterVector
34+
from processing.core.outputs import OutputVector
35+
from processing.tools import dataobjects, vector
36+
37+
from .fieldsmapping import ParameterFieldsMapping
38+
from .ui.FieldsMapperDialogs import (FieldsMapperParametersDialog,
39+
FieldsMapperModelerParametersDialog)
40+
41+
class FieldsMapper(GeoAlgorithm):
42+
43+
INPUT_LAYER = 'INPUT_LAYER'
44+
FIELDS_MAPPING = 'FIELDS_MAPPING'
45+
OUTPUT_LAYER = 'OUTPUT_LAYER'
46+
47+
def __init__(self):
48+
GeoAlgorithm.__init__(self)
49+
self.mapping = None
50+
51+
def defineCharacteristics(self):
52+
self.name = 'Refactor fields'
53+
self.group = 'Vector table tools'
54+
self.addParameter(ParameterVector(self.INPUT_LAYER,
55+
self.tr('Input layer'),
56+
[ParameterVector.VECTOR_TYPE_ANY], False))
57+
self.addParameter(ParameterFieldsMapping(self.FIELDS_MAPPING,
58+
self.tr('Fields mapping'), self.INPUT_LAYER))
59+
self.addOutput(OutputVector(self.OUTPUT_LAYER,
60+
self.tr('Output layer')))
61+
62+
def getCustomParametersDialog(self):
63+
return FieldsMapperParametersDialog(self)
64+
65+
def getCustomModelerParametersDialog(self, modelAlg, algIndex=None):
66+
return FieldsMapperModelerParametersDialog(self, modelAlg, algIndex)
67+
68+
def processAlgorithm(self, progress):
69+
layer = self.getParameterValue(self.INPUT_LAYER)
70+
mapping = self.getParameterValue(self.FIELDS_MAPPING)
71+
output = self.getOutputFromName(self.OUTPUT_LAYER)
72+
73+
layer = dataobjects.getObjectFromUri(layer)
74+
provider = layer.dataProvider()
75+
fields = []
76+
expressions = []
77+
for field_def in mapping:
78+
fields.append(QgsField(name=field_def['name'],
79+
type=field_def['type'],
80+
len=field_def['length'],
81+
prec=field_def['precision']))
82+
83+
expression = QgsExpression(field_def['expression'])
84+
if expression.hasParserError():
85+
raise GeoAlgorithmExecutionException(
86+
self.tr(u'Parser error in expression "{}": {}')
87+
.format(unicode(field_def['expression']),
88+
unicode(expression.parserErrorString())))
89+
expression.prepare(provider.fields())
90+
if expression.hasEvalError():
91+
raise GeoAlgorithmExecutionException(
92+
self.tr(u'Evaluation error in expression "{}": {}')
93+
.format(unicode(field_def['expression']),
94+
unicode(expression.evalErrorString())))
95+
expressions.append(expression)
96+
97+
writer = output.getVectorWriter(fields,
98+
provider.geometryType(),
99+
layer.crs())
100+
101+
# Create output vector layer with new attributes
102+
error = ''
103+
calculationSuccess = True
104+
inFeat = QgsFeature()
105+
outFeat = QgsFeature()
106+
features = vector.features(layer)
107+
count = len(features)
108+
for current, inFeat in enumerate(features):
109+
rownum = current + 1
110+
111+
outFeat.setGeometry(inFeat.geometry())
112+
113+
attrs = []
114+
for i in xrange(0, len(mapping)):
115+
field_def = mapping[i]
116+
expression = expressions[i]
117+
expression.setCurrentRowNumber(rownum)
118+
value = expression.evaluate(inFeat)
119+
if expression.hasEvalError():
120+
calculationSuccess = False
121+
error = expression.evalErrorString()
122+
break
123+
124+
attrs.append(value)
125+
outFeat.setAttributes(attrs)
126+
127+
writer.addFeature(outFeat)
128+
129+
current += 1
130+
progress.setPercentage(100 * current / float(count))
131+
132+
del writer
133+
134+
if not calculationSuccess:
135+
raise GeoAlgorithmExecutionException(
136+
self.tr('An error occurred while evaluating the calculation'
137+
' string:\n') + error)

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
from SelectByExpression import SelectByExpression
126126
from HypsometricCurves import HypsometricCurves
127127
from SplitLinesWithLines import SplitLinesWithLines
128+
from processing.algs.qgis.FieldsMapper import FieldsMapper
128129

129130
import processing.resources_rc
130131

@@ -171,6 +172,7 @@ def __init__(self):
171172
SetVectorStyle(), SetRasterStyle(),
172173
SelectByExpression(), HypsometricCurves(),
173174
SplitLinesWithLines(), CreateConstantRaster(),
175+
FieldsMapper(),
174176
]
175177

176178
if hasMatplotlib:
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
FieldsMapper.py
6+
---------------------
7+
Date : October 2014
8+
Copyright : (C) 2014 by Arnaud Morvan
9+
Email : arnaud dot morvan at camptocamp dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Arnaud Morvan'
21+
__date__ = 'October 2014'
22+
__copyright__ = '(C) 2014, Arnaud Morvan'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
29+
from processing.core.parameters import Parameter
30+
31+
32+
class ParameterFieldsMapping(Parameter):
33+
34+
def __init__(self, name='', description='', parent=None):
35+
Parameter.__init__(self, name, description)
36+
self.parent = parent
37+
self.value = []
38+
39+
def getValueAsCommandLineParameter(self):
40+
return '"' + unicode(self.value) + '"'
41+
42+
def setValue(self, value):
43+
if value is None:
44+
return False
45+
if isinstance(value, list):
46+
self.value = value
47+
return True
48+
if isinstance(value, unicode):
49+
try:
50+
self.value = eval(value)
51+
return True
52+
except Exception as e:
53+
print unicode(e) # display error in console
54+
return False
55+
return False
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
FieldsMapper.py
6+
---------------------
7+
Date : October 2014
8+
Copyright : (C) 2014 by Arnaud Morvan
9+
Email : arnaud dot morvan at camptocamp dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Arnaud Morvan'
21+
__date__ = 'October 2014'
22+
__copyright__ = '(C) 2014, Arnaud Morvan'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
29+
from PyQt4.QtGui import QComboBox, QSpacerItem
30+
31+
from processing.core.parameters import ParameterVector
32+
from processing.tools import dataobjects
33+
from processing.gui.ParametersPanel import ParametersPanel
34+
from processing.gui.AlgorithmDialog import AlgorithmDialog, AlgorithmDialogBase
35+
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
36+
37+
from processing.algs.qgis.fieldsmapping import ParameterFieldsMapping
38+
from .FieldsMappingPanel import FieldsMappingPanel
39+
40+
41+
class FieldsMapperParametersPanel(ParametersPanel):
42+
43+
def __init__(self, parent, alg):
44+
ParametersPanel.__init__(self, parent, alg)
45+
46+
item = self.layoutMain.itemAt(self.layoutMain.count() - 1)
47+
if isinstance(item, QSpacerItem):
48+
self.layoutMain.removeItem(item)
49+
item = None
50+
51+
def getWidgetFromParameter(self, param):
52+
if isinstance(param, ParameterFieldsMapping):
53+
item = FieldsMappingPanel()
54+
if param.parent in self.dependentItems:
55+
items = self.dependentItems[param.parent]
56+
else:
57+
items = []
58+
self.dependentItems[param.parent] = items
59+
items.append(param.name)
60+
parent = self.alg.getParameterFromName(param.parent)
61+
if isinstance(parent, ParameterVector):
62+
layers = dataobjects.getVectorLayers(parent.shapetype)
63+
else:
64+
layers = dataobjects.getTables()
65+
if len(layers) > 0:
66+
item.setLayer(layers[0])
67+
return item
68+
return ParametersPanel.getWidgetFromParameter(self, param)
69+
70+
def updateDependentFields(self):
71+
sender = self.sender()
72+
if not isinstance(sender, QComboBox):
73+
return
74+
if not sender.name in self.dependentItems:
75+
return
76+
layer = sender.itemData(sender.currentIndex())
77+
children = self.dependentItems[sender.name]
78+
for child in children:
79+
widget = self.valueItems[child]
80+
if isinstance(widget, FieldsMappingPanel):
81+
widget.setLayer(layer)
82+
83+
def somethingDependsOnThisParameter(self, parent):
84+
for param in self.alg.parameters:
85+
if isinstance(param, ParameterFieldsMapping):
86+
if param.parent == parent.name:
87+
return True
88+
return False
89+
90+
91+
class FieldsMapperParametersDialog(AlgorithmDialog):
92+
def __init__(self, alg):
93+
AlgorithmDialogBase.__init__(self, alg)
94+
95+
self.alg = alg
96+
97+
self.mainWidget = FieldsMapperParametersPanel(self, alg)
98+
self.setMainWidget()
99+
100+
def setParamValue(self, param, widget, alg=None):
101+
if isinstance(param, ParameterFieldsMapping):
102+
return param.setValue(widget.value())
103+
return AlgorithmDialog.setParamValue(self, param, widget, alg)
104+
105+
106+
class FieldsMapperModelerParametersDialog(ModelerParametersDialog):
107+
108+
def __init__(self, alg, model, algName=None):
109+
ModelerParametersDialog.__init__(self, alg, model, algName)
110+
111+
paramsLayout = self.paramPanel.layout()
112+
item = paramsLayout.itemAt(paramsLayout.count() - 1)
113+
if isinstance(item, QSpacerItem):
114+
paramsLayout.removeItem(item)
115+
item = None
116+
117+
def getWidgetFromParameter(self, param):
118+
if isinstance(param, ParameterFieldsMapping):
119+
return FieldsMappingPanel()
120+
return ModelerParametersDialog.getWidgetFromParameter(self, param)
121+
122+
def setPreviousValues(self):
123+
ModelerParametersDialog.setPreviousValues(self)
124+
if self._algName is not None:
125+
alg = self.model.algs[self._algName]
126+
for param in alg.algorithm.parameters:
127+
if isinstance(param, ParameterFieldsMapping):
128+
widget = self.valueItems[param.name]
129+
value = alg.params[param.name]
130+
if isinstance(value, unicode):
131+
# convert to list because of ModelerAlgorithme.resolveValue behavior with lists
132+
value = eval(value)
133+
widget.setValue(value)
134+
135+
def setParamValue(self, alg, param, widget):
136+
if isinstance(param, ParameterFieldsMapping):
137+
# convert to unicode because of ModelerAlgorithme.resolveValue behavior with lists
138+
alg.params[param.name] = unicode(widget.value())
139+
return True
140+
return ModelerParametersDialog.setParamValue(self, alg, param, widget)

‎python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py

Lines changed: 466 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Form implementation generated from reading ui file 'widgetFieldsMapping.ui'
4+
#
5+
# Created: Tue Jan 20 10:14:41 2015
6+
# by: PyQt4 UI code generator 4.10.4
7+
#
8+
# WARNING! All changes made in this file will be lost!
9+
10+
from PyQt4 import QtCore, QtGui
11+
12+
try:
13+
_fromUtf8 = QtCore.QString.fromUtf8
14+
except AttributeError:
15+
def _fromUtf8(s):
16+
return s
17+
18+
try:
19+
_encoding = QtGui.QApplication.UnicodeUTF8
20+
def _translate(context, text, disambig):
21+
return QtGui.QApplication.translate(context, text, disambig, _encoding)
22+
except AttributeError:
23+
def _translate(context, text, disambig):
24+
return QtGui.QApplication.translate(context, text, disambig)
25+
26+
class Ui_Form(object):
27+
def setupUi(self, Form):
28+
Form.setObjectName(_fromUtf8("Form"))
29+
Form.resize(590, 552)
30+
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
31+
sizePolicy.setHorizontalStretch(0)
32+
sizePolicy.setVerticalStretch(0)
33+
sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
34+
Form.setSizePolicy(sizePolicy)
35+
self.verticalLayout_2 = QtGui.QVBoxLayout(Form)
36+
self.verticalLayout_2.setMargin(0)
37+
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
38+
self.horizontalLayout = QtGui.QHBoxLayout()
39+
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
40+
self.fieldsView = QtGui.QTableView(Form)
41+
self.fieldsView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
42+
self.fieldsView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
43+
self.fieldsView.setObjectName(_fromUtf8("fieldsView"))
44+
self.horizontalLayout.addWidget(self.fieldsView)
45+
self.buttonLayout = QtGui.QVBoxLayout()
46+
self.buttonLayout.setObjectName(_fromUtf8("buttonLayout"))
47+
self.addButton = QtGui.QToolButton(Form)
48+
self.addButton.setObjectName(_fromUtf8("addButton"))
49+
self.buttonLayout.addWidget(self.addButton)
50+
self.deleteButton = QtGui.QToolButton(Form)
51+
self.deleteButton.setObjectName(_fromUtf8("deleteButton"))
52+
self.buttonLayout.addWidget(self.deleteButton)
53+
self.upButton = QtGui.QToolButton(Form)
54+
self.upButton.setObjectName(_fromUtf8("upButton"))
55+
self.buttonLayout.addWidget(self.upButton)
56+
self.downButton = QtGui.QToolButton(Form)
57+
self.downButton.setObjectName(_fromUtf8("downButton"))
58+
self.buttonLayout.addWidget(self.downButton)
59+
self.resetButton = QtGui.QToolButton(Form)
60+
self.resetButton.setObjectName(_fromUtf8("resetButton"))
61+
self.buttonLayout.addWidget(self.resetButton)
62+
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
63+
self.buttonLayout.addItem(spacerItem)
64+
self.horizontalLayout.addLayout(self.buttonLayout)
65+
self.verticalLayout_2.addLayout(self.horizontalLayout)
66+
self.loadFromLayerLayout = QtGui.QHBoxLayout()
67+
self.loadFromLayerLayout.setObjectName(_fromUtf8("loadFromLayerLayout"))
68+
self.loadFromLayerLabel = QtGui.QLabel(Form)
69+
self.loadFromLayerLabel.setObjectName(_fromUtf8("loadFromLayerLabel"))
70+
self.loadFromLayerLayout.addWidget(self.loadFromLayerLabel)
71+
self.layerCombo = QtGui.QComboBox(Form)
72+
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
73+
sizePolicy.setHorizontalStretch(0)
74+
sizePolicy.setVerticalStretch(0)
75+
sizePolicy.setHeightForWidth(self.layerCombo.sizePolicy().hasHeightForWidth())
76+
self.layerCombo.setSizePolicy(sizePolicy)
77+
self.layerCombo.setObjectName(_fromUtf8("layerCombo"))
78+
self.loadFromLayerLayout.addWidget(self.layerCombo)
79+
self.loadLayerFieldsButton = QtGui.QPushButton(Form)
80+
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
81+
sizePolicy.setHorizontalStretch(0)
82+
sizePolicy.setVerticalStretch(0)
83+
sizePolicy.setHeightForWidth(self.loadLayerFieldsButton.sizePolicy().hasHeightForWidth())
84+
self.loadLayerFieldsButton.setSizePolicy(sizePolicy)
85+
self.loadLayerFieldsButton.setObjectName(_fromUtf8("loadLayerFieldsButton"))
86+
self.loadFromLayerLayout.addWidget(self.loadLayerFieldsButton)
87+
self.verticalLayout_2.addLayout(self.loadFromLayerLayout)
88+
89+
self.retranslateUi(Form)
90+
QtCore.QMetaObject.connectSlotsByName(Form)
91+
92+
def retranslateUi(self, Form):
93+
Form.setWindowTitle(_translate("Form", "Fields", None))
94+
self.addButton.setToolTip(_translate("Form", "Add new field", None))
95+
self.addButton.setText(_translate("Form", "add", None))
96+
self.deleteButton.setToolTip(_translate("Form", "Delete selected field", None))
97+
self.deleteButton.setText(_translate("Form", "delete", None))
98+
self.upButton.setToolTip(_translate("Form", "Move selected field up", None))
99+
self.upButton.setText(_translate("Form", "up", None))
100+
self.downButton.setToolTip(_translate("Form", "Move selected field down", None))
101+
self.downButton.setText(_translate("Form", "down", None))
102+
self.resetButton.setToolTip(_translate("Form", "Reset all fields", None))
103+
self.resetButton.setText(_translate("Form", "reset", None))
104+
self.loadFromLayerLabel.setText(_translate("Form", "Load fields from layer", None))
105+
self.loadLayerFieldsButton.setToolTip(_translate("Form", "Load fields from selected layer", None))
106+
self.loadLayerFieldsButton.setText(_translate("Form", "Load fields", None))
107+
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>Form</class>
4+
<widget class="QWidget" name="Form">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>590</width>
10+
<height>552</height>
11+
</rect>
12+
</property>
13+
<property name="sizePolicy">
14+
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
15+
<horstretch>0</horstretch>
16+
<verstretch>0</verstretch>
17+
</sizepolicy>
18+
</property>
19+
<property name="windowTitle">
20+
<string>Fields</string>
21+
</property>
22+
<layout class="QVBoxLayout" name="verticalLayout_2">
23+
<property name="margin">
24+
<number>0</number>
25+
</property>
26+
<item>
27+
<layout class="QHBoxLayout" name="horizontalLayout">
28+
<item>
29+
<widget class="QTableView" name="fieldsView">
30+
<property name="selectionMode">
31+
<enum>QAbstractItemView::SingleSelection</enum>
32+
</property>
33+
<property name="selectionBehavior">
34+
<enum>QAbstractItemView::SelectRows</enum>
35+
</property>
36+
</widget>
37+
</item>
38+
<item>
39+
<layout class="QVBoxLayout" name="buttonLayout">
40+
<item>
41+
<widget class="QToolButton" name="addButton">
42+
<property name="toolTip">
43+
<string>Add new field</string>
44+
</property>
45+
<property name="text">
46+
<string>add</string>
47+
</property>
48+
</widget>
49+
</item>
50+
<item>
51+
<widget class="QToolButton" name="deleteButton">
52+
<property name="toolTip">
53+
<string>Delete selected field</string>
54+
</property>
55+
<property name="text">
56+
<string>delete</string>
57+
</property>
58+
</widget>
59+
</item>
60+
<item>
61+
<widget class="QToolButton" name="upButton">
62+
<property name="toolTip">
63+
<string>Move selected field up</string>
64+
</property>
65+
<property name="text">
66+
<string>up</string>
67+
</property>
68+
</widget>
69+
</item>
70+
<item>
71+
<widget class="QToolButton" name="downButton">
72+
<property name="toolTip">
73+
<string>Move selected field down</string>
74+
</property>
75+
<property name="text">
76+
<string>down</string>
77+
</property>
78+
</widget>
79+
</item>
80+
<item>
81+
<widget class="QToolButton" name="resetButton">
82+
<property name="toolTip">
83+
<string>Reset all fields</string>
84+
</property>
85+
<property name="text">
86+
<string>reset</string>
87+
</property>
88+
</widget>
89+
</item>
90+
<item>
91+
<spacer name="verticalSpacer">
92+
<property name="orientation">
93+
<enum>Qt::Vertical</enum>
94+
</property>
95+
<property name="sizeHint" stdset="0">
96+
<size>
97+
<width>20</width>
98+
<height>40</height>
99+
</size>
100+
</property>
101+
</spacer>
102+
</item>
103+
</layout>
104+
</item>
105+
</layout>
106+
</item>
107+
<item>
108+
<layout class="QHBoxLayout" name="loadFromLayerLayout">
109+
<item>
110+
<widget class="QLabel" name="loadFromLayerLabel">
111+
<property name="text">
112+
<string>Load fields from layer</string>
113+
</property>
114+
</widget>
115+
</item>
116+
<item>
117+
<widget class="QComboBox" name="layerCombo">
118+
<property name="sizePolicy">
119+
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
120+
<horstretch>0</horstretch>
121+
<verstretch>0</verstretch>
122+
</sizepolicy>
123+
</property>
124+
</widget>
125+
</item>
126+
<item>
127+
<widget class="QPushButton" name="loadLayerFieldsButton">
128+
<property name="sizePolicy">
129+
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
130+
<horstretch>0</horstretch>
131+
<verstretch>0</verstretch>
132+
</sizepolicy>
133+
</property>
134+
<property name="toolTip">
135+
<string>Load fields from selected layer</string>
136+
</property>
137+
<property name="text">
138+
<string>Load fields</string>
139+
</property>
140+
</widget>
141+
</item>
142+
</layout>
143+
</item>
144+
</layout>
145+
</widget>
146+
<resources/>
147+
<connections/>
148+
</ui>

0 commit comments

Comments
 (0)
Please sign in to comment.