mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
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:
commit
70e2696be5
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user