Skip to content
Permalink
Browse files

Merge pull request #2936 from sept-en/master

[processing] fix filter in Processing settings dialog. Add ability to specify mininal required number of items for MultipleInput parameter (fix #11469, fix #12580)
  • Loading branch information
alexbruy committed Mar 23, 2016
2 parents 4b57964 + f0be045 commit 70e2696be5cbcbdfed436109ab537a443e343fe7
@@ -25,7 +25,11 @@

__revision__ = '$Format:%H$'

from qgis.core import QGis, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPoint
from qgis.core import QGis
from qgis.core import QgsCoordinateReferenceSystem
from qgis.core import QgsFeature
from qgis.core import QgsGeometry
from qgis.core import QgsPoint
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import ParameterTable
from processing.core.parameters import ParameterTableField
@@ -31,8 +31,17 @@
from processing.core.ProcessingConfig import ProcessingConfig
from processing.core.ProcessingLog import ProcessingLog
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterRaster, ParameterVector, ParameterTable, ParameterMultipleInput, ParameterBoolean, ParameterFixedTable, ParameterExtent, ParameterNumber, ParameterSelection
from processing.core.outputs import OutputRaster, OutputVector
from processing.core.parameters import ParameterRaster
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterTable
from processing.core.parameters import ParameterMultipleInput
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterFixedTable
from processing.core.parameters import ParameterExtent
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterSelection
from processing.core.outputs import OutputRaster
from processing.core.outputs import OutputVector
from . import SagaUtils
from processing.tools import dataobjects
from processing.tools.system import getTempFilename
@@ -29,9 +29,12 @@
import sys
import os

from processing.tools.vector import resolveFieldIndex, features
from processing.tools.vector import resolveFieldIndex
from processing.tools.vector import features
from PyQt.QtCore import QCoreApplication
from qgis.core import QgsRasterLayer, QgsVectorLayer
from qgis.core import QgsRasterLayer
from qgis.core import QgsVectorLayer
from processing.tools.system import isWindows
from processing.tools import dataobjects


@@ -338,6 +341,38 @@ def __init__(self, name='', description='', datatype=-1, optional=False):
ParameterDataObject.__init__(self, name, description, None, optional)
self.datatype = int(float(datatype))
self.exported = None
self.minNumInputs = 0

""" Set minimum required number of inputs for parameter
By default minimal number of inputs is set to 1
@type _minNumInputs: numeric type or None
@param _minNumInputs: required minimum number of inputs for parameter. \
If user will pass None as parameter, we will use default minimal number of inputs (1)
@return: result, if the minimum number of inputs were set.
"""

def setMinNumInputs(self, _minNumInputs):
if _minNumInputs is None:
self.minNumInputs = 0
return True

if _minNumInputs < 1 and not self.optional:
# dont allow to set negative or null number of inputs if parameter isn't optional
return False

self.minNumInputs = int(_minNumInputs)
return True

""" Get minimum required number of inputs for parameter
@return: minimum number of inputs required for this parameter
@see: setMinNumInputs()
"""

def getMinNumInputs(self):
return self.minNumInputs

def setValue(self, obj):
self.exported = None
@@ -350,17 +385,23 @@ def setValue(self, obj):
if isinstance(obj, list):
if len(obj) == 0:
if self.optional:
self.value = None
return True
else:
return False
# prevent setting value if we didn't provide required minimal number of inputs
elif len(obj) < self.minNumInputs:
return False

self.value = ";".join([self.getAsString(lay) for lay in obj])
return True
else:
self.value = unicode(obj)
return True

def getSafeExportedLayers(self):
"""Returns not the value entered by the user, but a string with
"""
Returns not the value entered by the user, but a string with
semicolon-separated filenames which contains the data of the
selected layers, but saved in a standard format (currently
shapefiles for vector layers and GeoTiff for raster) so that
@@ -420,6 +461,7 @@ def getAsString(self, value):
if layer.name() == s:
return unicode(layer.dataProvider().dataSourceUri())
return s

if self.datatype == ParameterMultipleInput.TYPE_FILE:
return unicode(value)
else:
@@ -28,14 +28,31 @@
import os

from PyQt import uic
from PyQt.QtCore import Qt, QEvent, QPyNullVariant
from PyQt.QtWidgets import QFileDialog, QDialog, QStyle, QMessageBox, QStyledItemDelegate, QLineEdit, QWidget, QToolButton, QHBoxLayout, QComboBox
from PyQt.QtGui import QIcon, QStandardItemModel, QStandardItem
from qgis.gui import QgsDoubleSpinBox, QgsSpinBox

from processing.core.ProcessingConfig import ProcessingConfig, Setting
from PyQt.QtCore import (Qt,
QEvent,
QPyNullVariant)
from PyQt.QtWidgets import (QFileDialog,
QDialog,
QStyle,
QMessageBox,
QStyledItemDelegate,
QLineEdit,
QWidget,
QToolButton,
QHBoxLayout,
QComboBox)
from PyQt.QtGui import (QIcon,
QStandardItemModel,
QStandardItem)

from qgis.gui import QgsDoubleSpinBox
from qgis.gui import QgsSpinBox

from processing.core.ProcessingConfig import ProcessingConfig
from processing.core.ProcessingConfig import Setting
from processing.core.Processing import Processing
from processing.gui.menus import updateMenus, menusSettingsGroup
from processing.gui.menus import updateMenus
from processing.gui.menus import menusSettingsGroup


pluginPath = os.path.split(os.path.dirname(__file__))[0]
@@ -65,22 +82,51 @@ def __init__(self, toolbox):
self.delegate = SettingDelegate()
self.tree.setItemDelegateForColumn(1, self.delegate)

self.searchBox.textChanged.connect(self.fillTree)
self.searchBox.textChanged.connect(self.textChanged)

self.fillTree()

self.tree.expanded.connect(self.adjustColumns)

def textChanged(self):
text = unicode(self.searchBox.text().lower())
self._filterItem(self.model.invisibleRootItem(), text)
if text:
self.tree.expandAll()
else:
self.tree.collapseAll()

def _filterItem(self, item, text):
if item.hasChildren():
show = False
for i in xrange(item.rowCount()):
child = item.child(i)
showChild = self._filterItem(child, text)
show = (showChild or show)
self.tree.setRowHidden(item.row(), item.index().parent(), not show)
return show

elif isinstance(item, QStandardItem):
hide = bool(text) and (text not in item.text().lower())
self.tree.setRowHidden(item.row(), item.index().parent(), hide)
return not hide

def fillTree(self):
self.fillTreeUsingProviders()

def fillTreeUsingProviders(self):
self.items = {}
self.model.clear()
self.model.setHorizontalHeaderLabels([self.tr('Setting'),
self.tr('Value')])

text = unicode(self.searchBox.text())
settings = ProcessingConfig.getSettings()

rootItem = self.model.invisibleRootItem()

"""
Filter 'General', 'Models' and 'Scripts' items
"""
priorityKeys = [self.tr('General'), self.tr('Models'), self.tr('Scripts')]
for group in priorityKeys:
groupItem = QStandardItem(group)
@@ -89,27 +135,29 @@ def fillTree(self):
groupItem.setEditable(False)
emptyItem = QStandardItem()
emptyItem.setEditable(False)

rootItem.insertRow(0, [groupItem, emptyItem])
# add menu item only if it has any search matches
for setting in settings[group]:
if setting.hidden or setting.name.startswith("MENU_"):
continue

if text == '' or text.lower() in setting.description.lower():
labelItem = QStandardItem(setting.description)
labelItem.setIcon(icon)
labelItem.setEditable(False)
self.items[setting] = SettingItem(setting)
groupItem.insertRow(0, [labelItem, self.items[setting]])

if text != '':
self.tree.expand(groupItem.index())
labelItem = QStandardItem(setting.description)
labelItem.setIcon(icon)
labelItem.setEditable(False)
self.items[setting] = SettingItem(setting)
groupItem.insertRow(0, [labelItem, self.items[setting]])

"""
Filter 'Providers' items
"""
providersItem = QStandardItem(self.tr('Providers'))
icon = QIcon(os.path.join(pluginPath, 'images', 'alg.png'))
providersItem.setIcon(icon)
providersItem.setEditable(False)
emptyItem = QStandardItem()
emptyItem.setEditable(False)

rootItem.insertRow(0, [providersItem, emptyItem])
for group in settings.keys():
if group in priorityKeys or group == menusSettingsGroup:
@@ -119,34 +167,41 @@ def fillTree(self):
icon = ProcessingConfig.getGroupIcon(group)
groupItem.setIcon(icon)
groupItem.setEditable(False)

for setting in settings[group]:
if setting.hidden:
continue

if text == '' or text.lower() in setting.description.lower():
labelItem = QStandardItem(setting.description)
labelItem.setIcon(icon)
labelItem.setEditable(False)
self.items[setting] = SettingItem(setting)
groupItem.insertRow(0, [labelItem, self.items[setting]])
labelItem = QStandardItem(setting.description)
labelItem.setIcon(icon)
labelItem.setEditable(False)
self.items[setting] = SettingItem(setting)
groupItem.insertRow(0, [labelItem, self.items[setting]])

emptyItem = QStandardItem()
emptyItem.setEditable(False)
providersItem.appendRow([groupItem, emptyItem])

"""
Filter 'Menus' items
"""
menusItem = QStandardItem(self.tr('Menus (requires restart)'))
icon = QIcon(os.path.join(pluginPath, 'images', 'menu.png'))
menusItem.setIcon(icon)
menusItem.setEditable(False)
emptyItem = QStandardItem()
emptyItem.setEditable(False)

rootItem.insertRow(0, [menusItem, emptyItem])

providers = Processing.providers
for provider in providers:
groupItem = QStandardItem(provider.getDescription())
providerDescription = provider.getDescription()
groupItem = QStandardItem(providerDescription)
icon = provider.getIcon()
groupItem.setIcon(icon)
groupItem.setEditable(False)

for alg in provider.algs:
labelItem = QStandardItem(alg.name)
labelItem.setIcon(icon)
@@ -156,9 +211,12 @@ def fillTree(self):
except:
continue
self.items[setting] = SettingItem(setting)

groupItem.insertRow(0, [labelItem, self.items[setting]])

emptyItem = QStandardItem()
emptyItem.setEditable(False)

menusItem.appendRow([groupItem, emptyItem])

self.tree.sortByColumn(0, Qt.AscendingOrder)
@@ -298,6 +298,19 @@ def testOptional(self):
self.assertFalse(parameter.setValue(None))
self.assertEqual(parameter.value, "myLayerFile.shp")

def testMultipleInput(self):
parameter = ParameterMultipleInput('myName', 'myDesc', optional=True)
self.assertTrue(parameter.setMinNumInputs(1))

parameter = ParameterMultipleInput('myName', 'myDesc', optional=False)
self.assertFalse(parameter.setMinNumInputs(0))

parameter.setMinNumInputs(2)
self.assertTrue(parameter.setValue(['myLayerFile.shp', 'myLayerFile2.shp']))

parameter.setMinNumInputs(3)
self.assertFalse(parameter.setValue(['myLayerFile.shp', 'myLayerFile2.shp']))

def testGetAsStringWhenRaster(self):
parameter = ParameterMultipleInput('myName', 'myDesc', datatype=ParameterMultipleInput.TYPE_RASTER)

0 comments on commit 70e2696

Please sign in to comment.
You can’t perform that action at this time.