Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[processing] ParameterTableMultipleField type added
This adds a widget with multiple column attributes selector
  • Loading branch information
mbernasocchi authored and m-kuhn committed Jun 3, 2016
1 parent 2dea7c8 commit f9ab722
Show file tree
Hide file tree
Showing 9 changed files with 527 additions and 67 deletions.
12 changes: 11 additions & 1 deletion python/plugins/processing/algs/r/RAlgorithm.py
Expand Up @@ -43,6 +43,7 @@
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterSelection
from processing.core.parameters import ParameterTableField
from processing.core.parameters import ParameterTableMultipleField
from processing.core.parameters import ParameterExtent
from processing.core.parameters import ParameterCrs
from processing.core.parameters import ParameterFile
Expand Down Expand Up @@ -231,6 +232,15 @@ def processInputParameterToken(self, token, name):
break
if found:
param = ParameterTableField(name, desc, field)
elif token.lower().strip().startswith('multiple field'):
field = token.strip()[len('multiple field') + 1:]
found = False
for p in self.parameters:
if p.name == field:
found = True
break
if found:
param = ParameterTableMultipleField(token, desc, field)
elif token.lower().strip() == 'extent':
param = ParameterExtent(name, desc)
elif token.lower().strip() == 'point':
Expand Down Expand Up @@ -401,7 +411,7 @@ def getImportCommands(self):
commands.append(param.name + ' = NULL')
elif isinstance(param, ParameterCrs):
commands.append(param.name + ' = "' + param.value + '"')
elif isinstance(param, (ParameterTableField, ParameterString,
elif isinstance(param, (ParameterTableField, ParameterTableMultipleField, ParameterString,
ParameterFile)):
commands.append(param.name + '="' + param.value + '"')
elif isinstance(param, (ParameterNumber, ParameterSelection)):
Expand Down
66 changes: 66 additions & 0 deletions python/plugins/processing/core/parameters.py
Expand Up @@ -898,6 +898,72 @@ def getAsScriptCode(self):
return '##' + self.name + '=' + param_type + self.parent


class ParameterTableMultipleField(Parameter):
"""A parameter representing several table fields.
Its value is a string with items separated by semicolons, each of
which represents the name of each field.
In a script you can use it with
##Fields=[optional] multiple field [number|string] Parentinput
In the batch runner simply use a string with items separated by
semicolons, each of which represents the name of each field.
see algs.qgis.DeleteColumn.py for an usage example
"""

DATA_TYPE_NUMBER = 0
DATA_TYPE_STRING = 1
DATA_TYPE_ANY = -1

def __init__(self, name='', description='', parent=None, datatype=-1,
optional=False):
Parameter.__init__(self, name, description, None, optional)
self.parent = parent
self.datatype = int(datatype)

def getValueAsCommandLineParameter(self):
return '"' + unicode(self.value) + '"' if self.value is not None else unicode(None)

def setValue(self, obj):
if obj is None:
if self.optional:
self.value = None
return True
return False

if isinstance(obj, list):
if len(obj) == 0:
if self.optional:
self.value = None
return True
return False
self.value = ";".join(obj)
return True
else:
self.value = unicode(obj)
return True

def __str__(self):
return self.name + ' <' + self.__module__.split('.')[-1] + ' from ' \
+ self.parent + '>'

def dataType(self):
if self.datatype == self.DATA_TYPE_NUMBER:
return 'numeric'
elif self.datatype == self.DATA_TYPE_STRING:
return 'string'
else:
return 'any'

def getAsScriptCode(self):
param_type = ''
if self.optional:
param_type += 'optional '
param_type += 'multiple field '
return '##' + self.name + '=' + param_type + self.parent


class ParameterVector(ParameterDataObject):

VECTOR_TYPE_POINT = 0
Expand Down
7 changes: 6 additions & 1 deletion python/plugins/processing/gui/AlgorithmDialog.py
Expand Up @@ -49,6 +49,7 @@
from processing.core.parameters import ParameterFixedTable
from processing.core.parameters import ParameterRange
from processing.core.parameters import ParameterTableField
from processing.core.parameters import ParameterTableMultipleField
from processing.core.parameters import ParameterMultipleInput
from processing.core.parameters import ParameterString
from processing.core.parameters import ParameterNumber
Expand Down Expand Up @@ -149,10 +150,14 @@ def setParamValue(self, param, widget, alg=None):
return param.setValue(widget.table)
elif isinstance(param, ParameterRange):
return param.setValue(widget.getValue())
if isinstance(param, ParameterTableField):
elif isinstance(param, ParameterTableField):
if param.optional and widget.currentIndex() == 0:
return param.setValue(None)
return param.setValue(widget.currentText())
elif isinstance(param, ParameterTableMultipleField):
if param.optional and len(list(widget.get_selected_items())) == 0:
return param.setValue(None)
return param.setValue(list(widget.get_selected_items()))
elif isinstance(param, ParameterMultipleInput):
if param.datatype == ParameterMultipleInput.TYPE_FILE:
return param.setValue(widget.selectedoptions)
Expand Down
218 changes: 218 additions & 0 deletions python/plugins/processing/gui/ListMultiselectWidget.py
@@ -0,0 +1,218 @@
"""
allows multiple selection in a large list
Contact : marco@opengis.ch
.. note:: 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__ = 'marco@opengis.ch'
__revision__ = '$Format:%H$'
__date__ = '9/07/2013'

from PyQt4 import QtGui, QtCore, Qt


class ListMultiSelectWidget(QtGui.QGroupBox):
"""Widget to show two parallel lists and move elements between the two
usage from code:
self.myWidget = ListMultiSelectWidget(title='myTitle')
self.myLayout.insertWidget(1, self.myWidget)
usage from designer:
insert a QGroupBox in your UI file
optionally give a title to the QGroupBox
promote it to ListMultiSelectWidget
"""

selection_changed = QtCore.pyqtSignal()

def __init__(self, parent=None, title=None):
QtGui.QGroupBox.__init__(self)
self.setTitle(title)

self.selected_widget = None
self.unselected_widget = None
self._setupUI()

# connect actions
self.select_all_btn.clicked.connect(self._select_all)
self.deselect_all_btn.clicked.connect(self._deselect_all)
self.select_btn.clicked.connect(self._select)
self.deselect_btn.clicked.connect(self._deselect)

self.unselected_widget.itemDoubleClicked.connect(self._select)
self.selected_widget.itemDoubleClicked.connect(self._deselect)

def get_selected_items(self):
"""
:return list with all the selected items text
"""
return self._get_items(self.selected_widget)

def get_unselected_items(self):
"""
:return list with all the unselected items text
"""
return self._get_items(self.unselected_widget)

def add_selected_items(self, items):
"""
:param items list of strings to be added in the selected list
"""
self._add_items(self.selected_widget, items)

def add_unselected_items(self, items):
"""
:param items list of strings to be added in the unselected list
"""
self._add_items(self.unselected_widget, items)

def set_selected_items(self, items):
"""
:param items list of strings to be set as the selected list
"""
self._set_items(self.selected_widget, items)

def set_unselected_items(self, items):
"""
:param items list of strings to be set as the unselected list
"""
self._set_items(self.unselected_widget, items)

def clear(self):
"""
removes all items from selected and unselected
"""
self.set_selected_items([])
self.set_unselected_items([])

def addItem(self, item):
"""
This is for Processing
:param item: string to be added in the unselected list
"""
self.add_unselected_items([item])

def addItems(self, items):
"""
This is for Processing
:param items: list of strings to be added in the unselected list
"""
self.add_unselected_items(items)

def _get_items(self, widget):
for i in range(widget.count()):
yield widget.item(i).text()

def _set_items(self, widget, items):
widget.clear()
self._add_items(widget, items)

def _add_items(self, widget, items):
widget.addItems(items)

def _select_all(self):
self.unselected_widget.selectAll()
self._do_move(self.unselected_widget, self.selected_widget)

def _deselect_all(self):
self.selected_widget.selectAll()
self._do_move(self.selected_widget, self.unselected_widget)

def _select(self):
self._do_move(self.unselected_widget, self.selected_widget)

def _deselect(self):
self._do_move(self.selected_widget, self.unselected_widget)

def _do_move(self, fromList, toList):
for item in fromList.selectedItems():
prev_from_item = fromList.item(fromList.row(item) - 1)
toList.addItem(fromList.takeItem(fromList.row(item)))
fromList.scrollToItem(prev_from_item)
self.selection_changed.emit()

def _setupUI(self):
self.setSizePolicy(
QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Ignored)

self.setMinimumHeight(180)

self.main_horizontal_layout = QtGui.QHBoxLayout(self)

italic_font = QtGui.QFont()
italic_font.setItalic(True)

# unselected widget
self.unselected_widget = QtGui.QListWidget(self)
self._set_list_widget_defaults(self.unselected_widget)
unselected_label = QtGui.QLabel()
unselected_label.setText('Unselected')
unselected_label.setAlignment(Qt.Qt.AlignCenter)
unselected_label.setFont(italic_font)
unselected_v_layout = QtGui.QVBoxLayout()
unselected_v_layout.addWidget(unselected_label)
unselected_v_layout.addWidget(self.unselected_widget)

# selected widget
self.selected_widget = QtGui.QListWidget(self)
self._set_list_widget_defaults(self.selected_widget)
selected_label = QtGui.QLabel()
selected_label.setText('Selected')
selected_label.setAlignment(Qt.Qt.AlignCenter)
selected_label.setFont(italic_font)
selected_v_layout = QtGui.QVBoxLayout()
selected_v_layout.addWidget(selected_label)
selected_v_layout.addWidget(self.selected_widget)

# buttons
self.buttons_vertical_layout = QtGui.QVBoxLayout()
self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1)

self.select_all_btn = SmallQPushButton('>>')
self.deselect_all_btn = SmallQPushButton('<<')
self.select_btn = SmallQPushButton('>')
self.deselect_btn = SmallQPushButton('<')
self.select_btn.setToolTip('Add the selected items')
self.deselect_btn.setToolTip('Remove the selected items')
self.select_all_btn.setToolTip('Add all')
self.deselect_all_btn.setToolTip('Remove all')

# add buttons
spacer_label = QtGui.QLabel() # pragmatic way to create a spacer with
# the same height of the labels on top
# of the lists, in order to align the
# buttons with the lists.
self.buttons_vertical_layout.addWidget(spacer_label)
self.buttons_vertical_layout.addWidget(self.select_btn)
self.buttons_vertical_layout.addWidget(self.deselect_btn)
self.buttons_vertical_layout.addWidget(self.select_all_btn)
self.buttons_vertical_layout.addWidget(self.deselect_all_btn)

# add sub widgets
self.main_horizontal_layout.addLayout(unselected_v_layout)
self.main_horizontal_layout.addLayout(self.buttons_vertical_layout)
self.main_horizontal_layout.addLayout(selected_v_layout)

def _set_list_widget_defaults(self, widget):
widget.setAlternatingRowColors(True)
widget.setSortingEnabled(True)
widget.setDragEnabled(True)
widget.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
widget.setDragDropOverwriteMode(False)
widget.setDefaultDropAction(QtCore.Qt.MoveAction)
widget.setSelectionMode(QtGui.QAbstractItemView.MultiSelection)


class SmallQPushButton(QtGui.QPushButton):
def __init__(self, text):
QtGui.QPushButton.__init__(self)
self.setText(text)
buttons_size_policy = QtGui.QSizePolicy(
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.setSizePolicy(buttons_size_policy)
self.setMaximumSize(QtCore.QSize(30, 30))

0 comments on commit f9ab722

Please sign in to comment.