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)
This commit is contained in:
Alexander Bruy 2016-03-23 17:23:08 +02:00
commit 70e2696be5
5 changed files with 156 additions and 30 deletions

View File

@ -25,7 +25,11 @@ __copyright__ = '(C) 2013, Victor Olaya'
__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

View File

@ -31,8 +31,17 @@ from .SagaAlgorithm212 import SagaAlgorithm212
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

View File

@ -29,9 +29,12 @@ __revision__ = '$Format:%H$'
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 @@ class ParameterMultipleInput(ParameterDataObject):
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,9 +385,14 @@ class ParameterMultipleInput(ParameterDataObject):
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:
@ -360,7 +400,8 @@ class ParameterMultipleInput(ParameterDataObject):
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 @@ class ParameterMultipleInput(ParameterDataObject):
if layer.name() == s:
return unicode(layer.dataProvider().dataSourceUri())
return s
if self.datatype == ParameterMultipleInput.TYPE_FILE:
return unicode(value)
else:

View File

@ -28,14 +28,31 @@ __revision__ = '$Format:%H$'
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 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 processing.core.ProcessingConfig import ProcessingConfig, Setting
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 @@ class ConfigDialog(BASE, WIDGET):
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 @@ class ConfigDialog(BASE, WIDGET):
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 @@ class ConfigDialog(BASE, WIDGET):
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 @@ class ConfigDialog(BASE, WIDGET):
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)

View File

@ -298,6 +298,19 @@ class ParameterMultipleInputTest(unittest.TestCase):
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)