GuiTest.py 16 KB

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