1# -*- coding: utf-8 -*- 2 3""" 4*************************************************************************** 5 ParametersTest 6 --------------------- 7 Date : August 2017 8 Copyright : (C) 2017 by Nyall Dawson 9 Email : nyall dot dawson at gmail dot com 10*************************************************************************** 11* * 12* This program is free software; you can redistribute it and/or modify * 13* it under the terms of the GNU General Public License as published by * 14* the Free Software Foundation; either version 2 of the License, or * 15* (at your option) any later version. * 16* * 17*************************************************************************** 18""" 19 20__author__ = 'Nyall Dawson' 21__date__ = 'August 2017' 22__copyright__ = '(C) 2017, Nyall Dawson' 23 24import os 25from qgis.testing import start_app, unittest 26from qgis.core import (QgsApplication, 27 QgsCoordinateReferenceSystem, 28 QgsProcessingParameterMatrix, 29 QgsProcessingOutputLayerDefinition, 30 QgsProcessingParameterFeatureSink, 31 QgsProcessingParameterFileDestination, 32 QgsProcessingParameterFolderDestination, 33 QgsProcessingParameterVectorDestination, 34 QgsProcessingParameterRasterDestination, 35 QgsProcessingParameterRange, 36 QgsFeature, 37 QgsProcessingModelAlgorithm, 38 QgsUnitTypes, 39 QgsProject) 40from qgis.analysis import QgsNativeAlgorithms 41 42from processing.gui.AlgorithmDialog import AlgorithmDialog 43from processing.gui.BatchAlgorithmDialog import BatchAlgorithmDialog 44from processing.modeler.ModelerParametersDialog import ModelerParametersDialog 45from processing.gui.wrappers import ( 46 BandWidgetWrapper, 47 BooleanWidgetWrapper, 48 CrsWidgetWrapper, 49 DistanceWidgetWrapper, 50 EnumWidgetWrapper, 51 ExpressionWidgetWrapper, 52 ExtentWidgetWrapper, 53 FeatureSourceWidgetWrapper, 54 FileWidgetWrapper, 55 FixedTableWidgetWrapper, 56 MapLayerWidgetWrapper, 57 MeshWidgetWrapper, 58 MultipleLayerWidgetWrapper, 59 NumberWidgetWrapper, 60 PointWidgetWrapper, 61 ProcessingConfig, 62 QgsProcessingFeatureSourceDefinition, 63 QgsProcessingParameterBand, 64 QgsProcessingParameterBoolean, 65 QgsProcessingParameterCrs, 66 QgsProcessingParameterDistance, 67 QgsProcessingParameterDuration, 68 QgsProcessingParameterEnum, 69 QgsProcessingParameterExpression, 70 QgsProcessingParameterExtent, 71 QgsProcessingParameterFeatureSource, 72 QgsProcessingParameterField, 73 QgsProcessingParameterFile, 74 QgsProcessingParameterMapLayer, 75 QgsProcessingParameterMeshLayer, 76 QgsProcessingParameterMultipleLayers, 77 QgsProcessingParameterNumber, 78 QgsProcessingParameterPoint, 79 QgsProcessingParameterRasterLayer, 80 QgsProcessingParameterString, 81 QgsProcessingParameterVectorLayer, 82 QgsVectorLayer, 83 RangeWidgetWrapper, 84 RasterWidgetWrapper, 85 StringWidgetWrapper, 86 TableFieldWidgetWrapper, 87 VectorLayerWidgetWrapper, 88 WidgetWrapperFactory, 89) 90 91start_app() 92QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms()) 93 94testDataPath = os.path.join(os.path.dirname(__file__), 'testdata') 95 96 97class AlgorithmDialogTest(unittest.TestCase): 98 99 def testCreation(self): 100 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 101 a = AlgorithmDialog(alg) 102 self.assertEqual(a.mainWidget().algorithm(), alg) 103 104 105class WrappersTest(unittest.TestCase): 106 107 @classmethod 108 def setUpClass(cls): 109 ProcessingConfig.initialize() 110 111 def checkConstructWrapper(self, param, expected_wrapper_class): 112 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 113 114 # algorithm dialog 115 dlg = AlgorithmDialog(alg) 116 wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg) 117 self.assertIsNotNone(wrapper) 118 self.assertIsInstance(wrapper, expected_wrapper_class) 119 self.assertEqual(wrapper.dialog, dlg) 120 self.assertIsNotNone(wrapper.widget) 121 wrapper.widget.deleteLater() 122 del wrapper.widget 123 del wrapper 124 125 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 126 # batch dialog 127 dlg = BatchAlgorithmDialog(alg) 128 wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg) 129 self.assertIsNotNone(wrapper) 130 self.assertIsInstance(wrapper, expected_wrapper_class) 131 self.assertEqual(wrapper.dialog, dlg) 132 self.assertIsNotNone(wrapper.widget) 133 134 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 135 136 # modeler dialog 137 model = QgsProcessingModelAlgorithm() 138 dlg = ModelerParametersDialog(alg, model) 139 wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg) 140 self.assertIsNotNone(wrapper) 141 self.assertIsInstance(wrapper, expected_wrapper_class) 142 self.assertEqual(wrapper.dialog, dlg) 143 self.assertIsNotNone(wrapper.widget) 144 145 wrapper.widget.deleteLater() 146 del wrapper.widget 147 148 def testBoolean(self): 149 self.checkConstructWrapper(QgsProcessingParameterBoolean('test'), BooleanWidgetWrapper) 150 151 def testCrs(self): 152 self.checkConstructWrapper(QgsProcessingParameterCrs('test'), CrsWidgetWrapper) 153 154 def testExtent(self): 155 self.checkConstructWrapper(QgsProcessingParameterExtent('test'), ExtentWidgetWrapper) 156 157 def testPoint(self): 158 self.checkConstructWrapper(QgsProcessingParameterPoint('test'), PointWidgetWrapper) 159 160 def testFile(self): 161 self.checkConstructWrapper(QgsProcessingParameterFile('test'), FileWidgetWrapper) 162 163 def testMultiInput(self): 164 self.checkConstructWrapper(QgsProcessingParameterMultipleLayers('test'), MultipleLayerWidgetWrapper) 165 166 def testRasterInput(self): 167 self.checkConstructWrapper(QgsProcessingParameterRasterLayer('test'), RasterWidgetWrapper) 168 169 def testEnum(self): 170 self.checkConstructWrapper(QgsProcessingParameterEnum('test'), EnumWidgetWrapper) 171 172 def testString(self): 173 self.checkConstructWrapper(QgsProcessingParameterString('test'), StringWidgetWrapper) 174 175 def testExpression(self): 176 self.checkConstructWrapper(QgsProcessingParameterExpression('test'), ExpressionWidgetWrapper) 177 178 def testVector(self): 179 self.checkConstructWrapper(QgsProcessingParameterVectorLayer('test'), VectorLayerWidgetWrapper) 180 181 def testField(self): 182 self.checkConstructWrapper(QgsProcessingParameterField('test'), TableFieldWidgetWrapper) 183 184 def testSource(self): 185 self.checkConstructWrapper(QgsProcessingParameterFeatureSource('test'), FeatureSourceWidgetWrapper) 186 187 # dummy layer 188 layer = QgsVectorLayer('Point', 'test', 'memory') 189 # need at least one feature in order to have a selection 190 layer.dataProvider().addFeature(QgsFeature()) 191 layer.selectAll() 192 193 self.assertTrue(layer.isValid()) 194 QgsProject.instance().addMapLayer(layer) 195 196 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 197 dlg = AlgorithmDialog(alg) 198 param = QgsProcessingParameterFeatureSource('test') 199 wrapper = FeatureSourceWidgetWrapper(param, dlg) 200 widget = wrapper.createWidget() 201 202 # check layer value 203 widget.show() 204 wrapper.setValue(layer.id()) 205 self.assertEqual(wrapper.value(), layer.id()) 206 207 # check selected only - expect a QgsProcessingFeatureSourceDefinition 208 wrapper.setValue(QgsProcessingFeatureSourceDefinition(layer.id(), True)) 209 value = wrapper.value() 210 self.assertIsInstance(value, QgsProcessingFeatureSourceDefinition) 211 self.assertTrue(value.selectedFeaturesOnly) 212 self.assertEqual(value.source.staticValue(), layer.id()) 213 214 # NOT selected only, expect a direct layer id or source value 215 wrapper.setValue(QgsProcessingFeatureSourceDefinition(layer.id(), False)) 216 value = wrapper.value() 217 self.assertEqual(value, layer.id()) 218 219 # with non-project layer 220 wrapper.setValue('/home/my_layer.shp') 221 value = wrapper.value() 222 self.assertEqual(value, '/home/my_layer.shp') 223 224 widget.deleteLater() 225 del widget 226 227 def testRange(self): 228 # minimal test to check if wrapper generate GUI for each processign context 229 self.checkConstructWrapper(QgsProcessingParameterRange('test'), RangeWidgetWrapper) 230 231 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 232 dlg = AlgorithmDialog(alg) 233 param = QgsProcessingParameterRange( 234 name='test', 235 description='test', 236 type=QgsProcessingParameterNumber.Double, 237 defaultValue="0.0,100.0") 238 239 wrapper = RangeWidgetWrapper(param, dlg) 240 widget = wrapper.createWidget() 241 242 # range values check 243 244 # check initial value 245 self.assertEqual(widget.getValue(), '0.0,100.0') 246 # check set/get 247 widget.setValue("100.0,200.0") 248 self.assertEqual(widget.getValue(), '100.0,200.0') 249 # check that min/max are mutually adapted 250 widget.setValue("200.0,100.0") 251 self.assertEqual(widget.getValue(), '100.0,100.0') 252 widget.spnMax.setValue(50) 253 self.assertEqual(widget.getValue(), '50.0,50.0') 254 widget.spnMin.setValue(100) 255 self.assertEqual(widget.getValue(), '100.0,100.0') 256 257 # check for integers 258 param = QgsProcessingParameterRange( 259 name='test', 260 description='test', 261 type=QgsProcessingParameterNumber.Integer, 262 defaultValue="0.1,100.1") 263 264 wrapper = RangeWidgetWrapper(param, dlg) 265 widget = wrapper.createWidget() 266 267 # range values check 268 269 # check initial value 270 self.assertEqual(widget.getValue(), '0.0,100.0') 271 # check rounding 272 widget.setValue("100.1,200.1") 273 self.assertEqual(widget.getValue(), '100.0,200.0') 274 widget.setValue("100.6,200.6") 275 self.assertEqual(widget.getValue(), '101.0,201.0') 276 # check set/get 277 widget.setValue("100.1,200.1") 278 self.assertEqual(widget.getValue(), '100.0,200.0') 279 # check that min/max are mutually adapted 280 widget.setValue("200.1,100.1") 281 self.assertEqual(widget.getValue(), '100.0,100.0') 282 widget.spnMax.setValue(50.1) 283 self.assertEqual(widget.getValue(), '50.0,50.0') 284 widget.spnMin.setValue(100.1) 285 self.assertEqual(widget.getValue(), '100.0,100.0') 286 287 def testMapLayer(self): 288 self.checkConstructWrapper(QgsProcessingParameterMapLayer('test'), MapLayerWidgetWrapper) 289 290 def testMeshLayer(self): 291 self.checkConstructWrapper(QgsProcessingParameterMeshLayer('test'), MeshWidgetWrapper) 292 293 def testDistance(self): 294 self.checkConstructWrapper(QgsProcessingParameterDistance('test'), DistanceWidgetWrapper) 295 296 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 297 dlg = AlgorithmDialog(alg) 298 param = QgsProcessingParameterDistance('test') 299 wrapper = DistanceWidgetWrapper(param, dlg) 300 widget = wrapper.createWidget() 301 302 # test units 303 widget.show() 304 305 # crs values 306 widget.setUnitParameterValue('EPSG:3111') 307 self.assertEqual(widget.label.text(), 'meters') 308 self.assertFalse(widget.warning_label.isVisible()) 309 self.assertTrue(widget.units_combo.isVisible()) 310 self.assertFalse(widget.label.isVisible()) 311 self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) 312 313 widget.setUnitParameterValue('EPSG:4326') 314 self.assertEqual(widget.label.text(), 'degrees') 315 self.assertTrue(widget.warning_label.isVisible()) 316 self.assertFalse(widget.units_combo.isVisible()) 317 self.assertTrue(widget.label.isVisible()) 318 319 widget.setUnitParameterValue(QgsCoordinateReferenceSystem('EPSG:3111')) 320 self.assertEqual(widget.label.text(), 'meters') 321 self.assertFalse(widget.warning_label.isVisible()) 322 self.assertTrue(widget.units_combo.isVisible()) 323 self.assertFalse(widget.label.isVisible()) 324 self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) 325 326 widget.setUnitParameterValue(QgsCoordinateReferenceSystem('EPSG:4326')) 327 self.assertEqual(widget.label.text(), 'degrees') 328 self.assertTrue(widget.warning_label.isVisible()) 329 self.assertFalse(widget.units_combo.isVisible()) 330 self.assertTrue(widget.label.isVisible()) 331 332 # layer values 333 vl = QgsVectorLayer("Polygon?crs=epsg:3111&field=pk:int", "vl", "memory") 334 widget.setUnitParameterValue(vl) 335 self.assertEqual(widget.label.text(), 'meters') 336 self.assertFalse(widget.warning_label.isVisible()) 337 self.assertTrue(widget.units_combo.isVisible()) 338 self.assertFalse(widget.label.isVisible()) 339 self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) 340 341 vl2 = QgsVectorLayer("Polygon?crs=epsg:4326&field=pk:int", "vl", "memory") 342 widget.setUnitParameterValue(vl2) 343 self.assertEqual(widget.label.text(), 'degrees') 344 self.assertTrue(widget.warning_label.isVisible()) 345 self.assertFalse(widget.units_combo.isVisible()) 346 self.assertTrue(widget.label.isVisible()) 347 348 # unresolvable values 349 widget.setUnitParameterValue(vl.id()) 350 self.assertEqual(widget.label.text(), '<unknown>') 351 self.assertFalse(widget.warning_label.isVisible()) 352 self.assertFalse(widget.units_combo.isVisible()) 353 self.assertTrue(widget.label.isVisible()) 354 355 # resolvable text value 356 QgsProject.instance().addMapLayer(vl) 357 widget.setUnitParameterValue(vl.id()) 358 self.assertEqual(widget.label.text(), 'meters') 359 self.assertFalse(widget.warning_label.isVisible()) 360 self.assertTrue(widget.units_combo.isVisible()) 361 self.assertFalse(widget.label.isVisible()) 362 self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) 363 364 widget.setValue(5) 365 self.assertEqual(widget.getValue(), 5) 366 widget.units_combo.setCurrentIndex(widget.units_combo.findData(QgsUnitTypes.DistanceKilometers)) 367 self.assertEqual(widget.getValue(), 5000) 368 widget.setValue(2) 369 self.assertEqual(widget.getValue(), 2000) 370 371 widget.setUnitParameterValue(vl.id()) 372 self.assertEqual(widget.getValue(), 2) 373 widget.setValue(5) 374 self.assertEqual(widget.getValue(), 5) 375 376 widget.deleteLater() 377 378 def testMatrix(self): 379 self.checkConstructWrapper(QgsProcessingParameterMatrix('test'), FixedTableWidgetWrapper) 380 381 alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') 382 dlg = AlgorithmDialog(alg) 383 param = QgsProcessingParameterMatrix('test', 'test', 2, True, ['x', 'y'], [['a', 'b'], ['c', 'd']]) 384 wrapper = FixedTableWidgetWrapper(param, dlg) 385 widget = wrapper.createWidget() 386 387 # check that default value is initially set 388 self.assertEqual(wrapper.value(), [['a', 'b'], ['c', 'd']]) 389 390 # test widget 391 widget.show() 392 wrapper.setValue([[1, 2], [3, 4]]) 393 self.assertEqual(wrapper.value(), [[1, 2], [3, 4]]) 394 395 widget.deleteLater() 396 397 def testNumber(self): 398 self.checkConstructWrapper(QgsProcessingParameterNumber('test'), NumberWidgetWrapper) 399 400 def testBand(self): 401 self.checkConstructWrapper(QgsProcessingParameterBand('test'), BandWidgetWrapper) 402 403 404if __name__ == '__main__': 405 unittest.main() 406