Skip to content

Commit f9ab722

Browse files
mbernasocchim-kuhn
authored andcommitted
[processing] ParameterTableMultipleField type added
This adds a widget with multiple column attributes selector
1 parent 2dea7c8 commit f9ab722

9 files changed

+527
-67
lines changed

python/plugins/processing/algs/r/RAlgorithm.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from processing.core.parameters import ParameterBoolean
4444
from processing.core.parameters import ParameterSelection
4545
from processing.core.parameters import ParameterTableField
46+
from processing.core.parameters import ParameterTableMultipleField
4647
from processing.core.parameters import ParameterExtent
4748
from processing.core.parameters import ParameterCrs
4849
from processing.core.parameters import ParameterFile
@@ -231,6 +232,15 @@ def processInputParameterToken(self, token, name):
231232
break
232233
if found:
233234
param = ParameterTableField(name, desc, field)
235+
elif token.lower().strip().startswith('multiple field'):
236+
field = token.strip()[len('multiple field') + 1:]
237+
found = False
238+
for p in self.parameters:
239+
if p.name == field:
240+
found = True
241+
break
242+
if found:
243+
param = ParameterTableMultipleField(token, desc, field)
234244
elif token.lower().strip() == 'extent':
235245
param = ParameterExtent(name, desc)
236246
elif token.lower().strip() == 'point':
@@ -401,7 +411,7 @@ def getImportCommands(self):
401411
commands.append(param.name + ' = NULL')
402412
elif isinstance(param, ParameterCrs):
403413
commands.append(param.name + ' = "' + param.value + '"')
404-
elif isinstance(param, (ParameterTableField, ParameterString,
414+
elif isinstance(param, (ParameterTableField, ParameterTableMultipleField, ParameterString,
405415
ParameterFile)):
406416
commands.append(param.name + '="' + param.value + '"')
407417
elif isinstance(param, (ParameterNumber, ParameterSelection)):

python/plugins/processing/core/parameters.py

+66
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,72 @@ def getAsScriptCode(self):
898898
return '##' + self.name + '=' + param_type + self.parent
899899

900900

901+
class ParameterTableMultipleField(Parameter):
902+
"""A parameter representing several table fields.
903+
Its value is a string with items separated by semicolons, each of
904+
which represents the name of each field.
905+
906+
In a script you can use it with
907+
##Fields=[optional] multiple field [number|string] Parentinput
908+
909+
In the batch runner simply use a string with items separated by
910+
semicolons, each of which represents the name of each field.
911+
912+
see algs.qgis.DeleteColumn.py for an usage example
913+
"""
914+
915+
DATA_TYPE_NUMBER = 0
916+
DATA_TYPE_STRING = 1
917+
DATA_TYPE_ANY = -1
918+
919+
def __init__(self, name='', description='', parent=None, datatype=-1,
920+
optional=False):
921+
Parameter.__init__(self, name, description, None, optional)
922+
self.parent = parent
923+
self.datatype = int(datatype)
924+
925+
def getValueAsCommandLineParameter(self):
926+
return '"' + unicode(self.value) + '"' if self.value is not None else unicode(None)
927+
928+
def setValue(self, obj):
929+
if obj is None:
930+
if self.optional:
931+
self.value = None
932+
return True
933+
return False
934+
935+
if isinstance(obj, list):
936+
if len(obj) == 0:
937+
if self.optional:
938+
self.value = None
939+
return True
940+
return False
941+
self.value = ";".join(obj)
942+
return True
943+
else:
944+
self.value = unicode(obj)
945+
return True
946+
947+
def __str__(self):
948+
return self.name + ' <' + self.__module__.split('.')[-1] + ' from ' \
949+
+ self.parent + '>'
950+
951+
def dataType(self):
952+
if self.datatype == self.DATA_TYPE_NUMBER:
953+
return 'numeric'
954+
elif self.datatype == self.DATA_TYPE_STRING:
955+
return 'string'
956+
else:
957+
return 'any'
958+
959+
def getAsScriptCode(self):
960+
param_type = ''
961+
if self.optional:
962+
param_type += 'optional '
963+
param_type += 'multiple field '
964+
return '##' + self.name + '=' + param_type + self.parent
965+
966+
901967
class ParameterVector(ParameterDataObject):
902968

903969
VECTOR_TYPE_POINT = 0

python/plugins/processing/gui/AlgorithmDialog.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from processing.core.parameters import ParameterFixedTable
5050
from processing.core.parameters import ParameterRange
5151
from processing.core.parameters import ParameterTableField
52+
from processing.core.parameters import ParameterTableMultipleField
5253
from processing.core.parameters import ParameterMultipleInput
5354
from processing.core.parameters import ParameterString
5455
from processing.core.parameters import ParameterNumber
@@ -149,10 +150,14 @@ def setParamValue(self, param, widget, alg=None):
149150
return param.setValue(widget.table)
150151
elif isinstance(param, ParameterRange):
151152
return param.setValue(widget.getValue())
152-
if isinstance(param, ParameterTableField):
153+
elif isinstance(param, ParameterTableField):
153154
if param.optional and widget.currentIndex() == 0:
154155
return param.setValue(None)
155156
return param.setValue(widget.currentText())
157+
elif isinstance(param, ParameterTableMultipleField):
158+
if param.optional and len(list(widget.get_selected_items())) == 0:
159+
return param.setValue(None)
160+
return param.setValue(list(widget.get_selected_items()))
156161
elif isinstance(param, ParameterMultipleInput):
157162
if param.datatype == ParameterMultipleInput.TYPE_FILE:
158163
return param.setValue(widget.selectedoptions)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
"""
2+
allows multiple selection in a large list
3+
4+
Contact : marco@opengis.ch
5+
6+
.. note:: This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation; either version 2 of the License, or
9+
(at your option) any later version.
10+
"""
11+
12+
__author__ = 'marco@opengis.ch'
13+
__revision__ = '$Format:%H$'
14+
__date__ = '9/07/2013'
15+
16+
from PyQt4 import QtGui, QtCore, Qt
17+
18+
19+
class ListMultiSelectWidget(QtGui.QGroupBox):
20+
"""Widget to show two parallel lists and move elements between the two
21+
22+
usage from code:
23+
self.myWidget = ListMultiSelectWidget(title='myTitle')
24+
self.myLayout.insertWidget(1, self.myWidget)
25+
usage from designer:
26+
insert a QGroupBox in your UI file
27+
optionally give a title to the QGroupBox
28+
promote it to ListMultiSelectWidget
29+
"""
30+
31+
selection_changed = QtCore.pyqtSignal()
32+
33+
def __init__(self, parent=None, title=None):
34+
QtGui.QGroupBox.__init__(self)
35+
self.setTitle(title)
36+
37+
self.selected_widget = None
38+
self.unselected_widget = None
39+
self._setupUI()
40+
41+
# connect actions
42+
self.select_all_btn.clicked.connect(self._select_all)
43+
self.deselect_all_btn.clicked.connect(self._deselect_all)
44+
self.select_btn.clicked.connect(self._select)
45+
self.deselect_btn.clicked.connect(self._deselect)
46+
47+
self.unselected_widget.itemDoubleClicked.connect(self._select)
48+
self.selected_widget.itemDoubleClicked.connect(self._deselect)
49+
50+
def get_selected_items(self):
51+
"""
52+
:return list with all the selected items text
53+
"""
54+
return self._get_items(self.selected_widget)
55+
56+
def get_unselected_items(self):
57+
"""
58+
:return list with all the unselected items text
59+
"""
60+
return self._get_items(self.unselected_widget)
61+
62+
def add_selected_items(self, items):
63+
"""
64+
:param items list of strings to be added in the selected list
65+
"""
66+
self._add_items(self.selected_widget, items)
67+
68+
def add_unselected_items(self, items):
69+
"""
70+
:param items list of strings to be added in the unselected list
71+
"""
72+
self._add_items(self.unselected_widget, items)
73+
74+
def set_selected_items(self, items):
75+
"""
76+
:param items list of strings to be set as the selected list
77+
"""
78+
self._set_items(self.selected_widget, items)
79+
80+
def set_unselected_items(self, items):
81+
"""
82+
:param items list of strings to be set as the unselected list
83+
"""
84+
self._set_items(self.unselected_widget, items)
85+
86+
def clear(self):
87+
"""
88+
removes all items from selected and unselected
89+
"""
90+
self.set_selected_items([])
91+
self.set_unselected_items([])
92+
93+
def addItem(self, item):
94+
"""
95+
This is for Processing
96+
:param item: string to be added in the unselected list
97+
"""
98+
self.add_unselected_items([item])
99+
100+
def addItems(self, items):
101+
"""
102+
This is for Processing
103+
:param items: list of strings to be added in the unselected list
104+
"""
105+
self.add_unselected_items(items)
106+
107+
def _get_items(self, widget):
108+
for i in range(widget.count()):
109+
yield widget.item(i).text()
110+
111+
def _set_items(self, widget, items):
112+
widget.clear()
113+
self._add_items(widget, items)
114+
115+
def _add_items(self, widget, items):
116+
widget.addItems(items)
117+
118+
def _select_all(self):
119+
self.unselected_widget.selectAll()
120+
self._do_move(self.unselected_widget, self.selected_widget)
121+
122+
def _deselect_all(self):
123+
self.selected_widget.selectAll()
124+
self._do_move(self.selected_widget, self.unselected_widget)
125+
126+
def _select(self):
127+
self._do_move(self.unselected_widget, self.selected_widget)
128+
129+
def _deselect(self):
130+
self._do_move(self.selected_widget, self.unselected_widget)
131+
132+
def _do_move(self, fromList, toList):
133+
for item in fromList.selectedItems():
134+
prev_from_item = fromList.item(fromList.row(item) - 1)
135+
toList.addItem(fromList.takeItem(fromList.row(item)))
136+
fromList.scrollToItem(prev_from_item)
137+
self.selection_changed.emit()
138+
139+
def _setupUI(self):
140+
self.setSizePolicy(
141+
QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Ignored)
142+
143+
self.setMinimumHeight(180)
144+
145+
self.main_horizontal_layout = QtGui.QHBoxLayout(self)
146+
147+
italic_font = QtGui.QFont()
148+
italic_font.setItalic(True)
149+
150+
# unselected widget
151+
self.unselected_widget = QtGui.QListWidget(self)
152+
self._set_list_widget_defaults(self.unselected_widget)
153+
unselected_label = QtGui.QLabel()
154+
unselected_label.setText('Unselected')
155+
unselected_label.setAlignment(Qt.Qt.AlignCenter)
156+
unselected_label.setFont(italic_font)
157+
unselected_v_layout = QtGui.QVBoxLayout()
158+
unselected_v_layout.addWidget(unselected_label)
159+
unselected_v_layout.addWidget(self.unselected_widget)
160+
161+
# selected widget
162+
self.selected_widget = QtGui.QListWidget(self)
163+
self._set_list_widget_defaults(self.selected_widget)
164+
selected_label = QtGui.QLabel()
165+
selected_label.setText('Selected')
166+
selected_label.setAlignment(Qt.Qt.AlignCenter)
167+
selected_label.setFont(italic_font)
168+
selected_v_layout = QtGui.QVBoxLayout()
169+
selected_v_layout.addWidget(selected_label)
170+
selected_v_layout.addWidget(self.selected_widget)
171+
172+
# buttons
173+
self.buttons_vertical_layout = QtGui.QVBoxLayout()
174+
self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1)
175+
176+
self.select_all_btn = SmallQPushButton('>>')
177+
self.deselect_all_btn = SmallQPushButton('<<')
178+
self.select_btn = SmallQPushButton('>')
179+
self.deselect_btn = SmallQPushButton('<')
180+
self.select_btn.setToolTip('Add the selected items')
181+
self.deselect_btn.setToolTip('Remove the selected items')
182+
self.select_all_btn.setToolTip('Add all')
183+
self.deselect_all_btn.setToolTip('Remove all')
184+
185+
# add buttons
186+
spacer_label = QtGui.QLabel() # pragmatic way to create a spacer with
187+
# the same height of the labels on top
188+
# of the lists, in order to align the
189+
# buttons with the lists.
190+
self.buttons_vertical_layout.addWidget(spacer_label)
191+
self.buttons_vertical_layout.addWidget(self.select_btn)
192+
self.buttons_vertical_layout.addWidget(self.deselect_btn)
193+
self.buttons_vertical_layout.addWidget(self.select_all_btn)
194+
self.buttons_vertical_layout.addWidget(self.deselect_all_btn)
195+
196+
# add sub widgets
197+
self.main_horizontal_layout.addLayout(unselected_v_layout)
198+
self.main_horizontal_layout.addLayout(self.buttons_vertical_layout)
199+
self.main_horizontal_layout.addLayout(selected_v_layout)
200+
201+
def _set_list_widget_defaults(self, widget):
202+
widget.setAlternatingRowColors(True)
203+
widget.setSortingEnabled(True)
204+
widget.setDragEnabled(True)
205+
widget.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
206+
widget.setDragDropOverwriteMode(False)
207+
widget.setDefaultDropAction(QtCore.Qt.MoveAction)
208+
widget.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)
209+
210+
211+
class SmallQPushButton(QtGui.QPushButton):
212+
def __init__(self, text):
213+
QtGui.QPushButton.__init__(self)
214+
self.setText(text)
215+
buttons_size_policy = QtGui.QSizePolicy(
216+
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
217+
self.setSizePolicy(buttons_size_policy)
218+
self.setMaximumSize(QtCore.QSize(30, 30))

0 commit comments

Comments
 (0)