2024-11-29 15:38:02 +01:00

476 lines
16 KiB
Python

"""
***************************************************************************
ParametersTest
---------------------
Date : August 2017
Copyright : (C) 2017 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
__author__ = "Nyall Dawson"
__date__ = "August 2017"
__copyright__ = "(C) 2017, Nyall Dawson"
import os
import unittest
from qgis.testing import start_app, QgisTestCase
from qgis.core import (
QgsApplication,
QgsCoordinateReferenceSystem,
QgsProcessingParameterMatrix,
QgsProcessingOutputLayerDefinition,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterFileDestination,
QgsProcessingParameterFolderDestination,
QgsProcessingParameterVectorDestination,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterRange,
QgsFeature,
QgsProcessingModelAlgorithm,
QgsUnitTypes,
QgsProject,
)
from qgis.analysis import QgsNativeAlgorithms
from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.gui.BatchAlgorithmDialog import BatchAlgorithmDialog
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
from processing.gui.wrappers import (
BandWidgetWrapper,
BooleanWidgetWrapper,
CrsWidgetWrapper,
DistanceWidgetWrapper,
EnumWidgetWrapper,
ExpressionWidgetWrapper,
ExtentWidgetWrapper,
FeatureSourceWidgetWrapper,
FileWidgetWrapper,
FixedTableWidgetWrapper,
MapLayerWidgetWrapper,
MeshWidgetWrapper,
MultipleLayerWidgetWrapper,
NumberWidgetWrapper,
PointWidgetWrapper,
ProcessingConfig,
QgsProcessingFeatureSourceDefinition,
QgsProcessingParameterBand,
QgsProcessingParameterBoolean,
QgsProcessingParameterCrs,
QgsProcessingParameterDistance,
QgsProcessingParameterDuration,
QgsProcessingParameterEnum,
QgsProcessingParameterExpression,
QgsProcessingParameterExtent,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
QgsProcessingParameterFile,
QgsProcessingParameterMapLayer,
QgsProcessingParameterMeshLayer,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterNumber,
QgsProcessingParameterPoint,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterString,
QgsProcessingParameterVectorLayer,
QgsVectorLayer,
RangeWidgetWrapper,
RasterWidgetWrapper,
StringWidgetWrapper,
TableFieldWidgetWrapper,
VectorLayerWidgetWrapper,
WidgetWrapperFactory,
)
start_app()
QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
testDataPath = os.path.join(os.path.dirname(__file__), "testdata")
class AlgorithmDialogTest(QgisTestCase):
def testCreation(self):
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
a = AlgorithmDialog(alg)
self.assertEqual(a.mainWidget().algorithm(), alg)
class WrappersTest(QgisTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
ProcessingConfig.initialize()
def checkConstructWrapper(self, param, expected_wrapper_class):
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
# algorithm dialog
dlg = AlgorithmDialog(alg)
wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg)
self.assertIsNotNone(wrapper)
self.assertIsInstance(wrapper, expected_wrapper_class)
self.assertEqual(wrapper.dialog, dlg)
self.assertIsNotNone(wrapper.widget)
wrapper.widget.deleteLater()
del wrapper.widget
del wrapper
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
# batch dialog
dlg = BatchAlgorithmDialog(alg)
wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg)
self.assertIsNotNone(wrapper)
self.assertIsInstance(wrapper, expected_wrapper_class)
self.assertEqual(wrapper.dialog, dlg)
self.assertIsNotNone(wrapper.widget)
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
# modeler dialog
model = QgsProcessingModelAlgorithm()
dlg = ModelerParametersDialog(alg, model)
wrapper = WidgetWrapperFactory.create_wrapper_from_class(param, dlg)
self.assertIsNotNone(wrapper)
self.assertIsInstance(wrapper, expected_wrapper_class)
self.assertEqual(wrapper.dialog, dlg)
self.assertIsNotNone(wrapper.widget)
wrapper.widget.deleteLater()
del wrapper.widget
def testBoolean(self):
self.checkConstructWrapper(
QgsProcessingParameterBoolean("test"), BooleanWidgetWrapper
)
def testCrs(self):
self.checkConstructWrapper(QgsProcessingParameterCrs("test"), CrsWidgetWrapper)
def testExtent(self):
self.checkConstructWrapper(
QgsProcessingParameterExtent("test"), ExtentWidgetWrapper
)
def testPoint(self):
self.checkConstructWrapper(
QgsProcessingParameterPoint("test"), PointWidgetWrapper
)
def testFile(self):
self.checkConstructWrapper(
QgsProcessingParameterFile("test"), FileWidgetWrapper
)
def testMultiInput(self):
self.checkConstructWrapper(
QgsProcessingParameterMultipleLayers("test"), MultipleLayerWidgetWrapper
)
def testRasterInput(self):
self.checkConstructWrapper(
QgsProcessingParameterRasterLayer("test"), RasterWidgetWrapper
)
def testEnum(self):
self.checkConstructWrapper(
QgsProcessingParameterEnum("test"), EnumWidgetWrapper
)
def testString(self):
self.checkConstructWrapper(
QgsProcessingParameterString("test"), StringWidgetWrapper
)
def testExpression(self):
self.checkConstructWrapper(
QgsProcessingParameterExpression("test"), ExpressionWidgetWrapper
)
def testVector(self):
self.checkConstructWrapper(
QgsProcessingParameterVectorLayer("test"), VectorLayerWidgetWrapper
)
def testField(self):
self.checkConstructWrapper(
QgsProcessingParameterField("test"), TableFieldWidgetWrapper
)
def testSource(self):
self.checkConstructWrapper(
QgsProcessingParameterFeatureSource("test"), FeatureSourceWidgetWrapper
)
# dummy layer
layer = QgsVectorLayer("Point", "test", "memory")
# need at least one feature in order to have a selection
layer.dataProvider().addFeature(QgsFeature())
layer.selectAll()
self.assertTrue(layer.isValid())
QgsProject.instance().addMapLayer(layer)
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
dlg = AlgorithmDialog(alg)
param = QgsProcessingParameterFeatureSource("test")
wrapper = FeatureSourceWidgetWrapper(param, dlg)
widget = wrapper.createWidget()
# check layer value
widget.show()
wrapper.setValue(layer.id())
self.assertEqual(wrapper.value(), layer.id())
# check selected only - expect a QgsProcessingFeatureSourceDefinition
wrapper.setValue(QgsProcessingFeatureSourceDefinition(layer.id(), True))
value = wrapper.value()
self.assertIsInstance(value, QgsProcessingFeatureSourceDefinition)
self.assertTrue(value.selectedFeaturesOnly)
self.assertEqual(value.source.staticValue(), layer.id())
# NOT selected only, expect a direct layer id or source value
wrapper.setValue(QgsProcessingFeatureSourceDefinition(layer.id(), False))
value = wrapper.value()
self.assertEqual(value, layer.id())
# with non-project layer
wrapper.setValue("/home/my_layer.shp")
value = wrapper.value()
self.assertEqual(value, "/home/my_layer.shp")
widget.deleteLater()
del widget
def testRange(self):
# minimal test to check if wrapper generate GUI for each processign context
self.checkConstructWrapper(
QgsProcessingParameterRange("test"), RangeWidgetWrapper
)
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
dlg = AlgorithmDialog(alg)
param = QgsProcessingParameterRange(
name="test",
description="test",
type=QgsProcessingParameterNumber.Type.Double,
defaultValue="0.0,100.0",
)
wrapper = RangeWidgetWrapper(param, dlg)
widget = wrapper.createWidget()
# range values check
# check initial value
self.assertEqual(widget.getValue(), "0.0,100.0")
# check set/get
widget.setValue("100.0,200.0")
self.assertEqual(widget.getValue(), "100.0,200.0")
# check that min/max are mutually adapted
widget.setValue("200.0,100.0")
self.assertEqual(widget.getValue(), "100.0,100.0")
widget.spnMax.setValue(50)
self.assertEqual(widget.getValue(), "50.0,50.0")
widget.spnMin.setValue(100)
self.assertEqual(widget.getValue(), "100.0,100.0")
# check for integers
param = QgsProcessingParameterRange(
name="test",
description="test",
type=QgsProcessingParameterNumber.Type.Integer,
defaultValue="0.1,100.1",
)
wrapper = RangeWidgetWrapper(param, dlg)
widget = wrapper.createWidget()
# range values check
# check initial value
self.assertEqual(widget.getValue(), "0.0,100.0")
# check rounding
widget.setValue("100.1,200.1")
self.assertEqual(widget.getValue(), "100.0,200.0")
widget.setValue("100.6,200.6")
self.assertEqual(widget.getValue(), "101.0,201.0")
# check set/get
widget.setValue("100.1,200.1")
self.assertEqual(widget.getValue(), "100.0,200.0")
# check that min/max are mutually adapted
widget.setValue("200.1,100.1")
self.assertEqual(widget.getValue(), "100.0,100.0")
widget.spnMax.setValue(50.1)
self.assertEqual(widget.getValue(), "50.0,50.0")
widget.spnMin.setValue(100.1)
self.assertEqual(widget.getValue(), "100.0,100.0")
def testMapLayer(self):
self.checkConstructWrapper(
QgsProcessingParameterMapLayer("test"), MapLayerWidgetWrapper
)
def testMeshLayer(self):
self.checkConstructWrapper(
QgsProcessingParameterMeshLayer("test"), MeshWidgetWrapper
)
def testDistance(self):
self.checkConstructWrapper(
QgsProcessingParameterDistance("test"), DistanceWidgetWrapper
)
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
dlg = AlgorithmDialog(alg)
param = QgsProcessingParameterDistance("test")
wrapper = DistanceWidgetWrapper(param, dlg)
widget = wrapper.createWidget()
# test units
widget.show()
# crs values
widget.setUnitParameterValue("EPSG:3111")
self.assertEqual(widget.label.text(), "meters")
self.assertFalse(widget.warning_label.isVisible())
self.assertTrue(widget.units_combo.isVisible())
self.assertFalse(widget.label.isVisible())
self.assertEqual(
widget.units_combo.currentData(), QgsUnitTypes.DistanceUnit.DistanceMeters
)
widget.setUnitParameterValue("EPSG:4326")
self.assertEqual(widget.label.text(), "degrees")
self.assertTrue(widget.warning_label.isVisible())
self.assertFalse(widget.units_combo.isVisible())
self.assertTrue(widget.label.isVisible())
widget.setUnitParameterValue(QgsCoordinateReferenceSystem("EPSG:3111"))
self.assertEqual(widget.label.text(), "meters")
self.assertFalse(widget.warning_label.isVisible())
self.assertTrue(widget.units_combo.isVisible())
self.assertFalse(widget.label.isVisible())
self.assertEqual(
widget.units_combo.currentData(), QgsUnitTypes.DistanceUnit.DistanceMeters
)
widget.setUnitParameterValue(QgsCoordinateReferenceSystem("EPSG:4326"))
self.assertEqual(widget.label.text(), "degrees")
self.assertTrue(widget.warning_label.isVisible())
self.assertFalse(widget.units_combo.isVisible())
self.assertTrue(widget.label.isVisible())
# layer values
vl = QgsVectorLayer("Polygon?crs=epsg:3111&field=pk:int", "vl", "memory")
widget.setUnitParameterValue(vl)
self.assertEqual(widget.label.text(), "meters")
self.assertFalse(widget.warning_label.isVisible())
self.assertTrue(widget.units_combo.isVisible())
self.assertFalse(widget.label.isVisible())
self.assertEqual(
widget.units_combo.currentData(), QgsUnitTypes.DistanceUnit.DistanceMeters
)
vl2 = QgsVectorLayer("Polygon?crs=epsg:4326&field=pk:int", "vl", "memory")
widget.setUnitParameterValue(vl2)
self.assertEqual(widget.label.text(), "degrees")
self.assertTrue(widget.warning_label.isVisible())
self.assertFalse(widget.units_combo.isVisible())
self.assertTrue(widget.label.isVisible())
# unresolvable values
widget.setUnitParameterValue(vl.id())
self.assertEqual(widget.label.text(), "<unknown>")
self.assertFalse(widget.warning_label.isVisible())
self.assertFalse(widget.units_combo.isVisible())
self.assertTrue(widget.label.isVisible())
# resolvable text value
QgsProject.instance().addMapLayer(vl)
widget.setUnitParameterValue(vl.id())
self.assertEqual(widget.label.text(), "meters")
self.assertFalse(widget.warning_label.isVisible())
self.assertTrue(widget.units_combo.isVisible())
self.assertFalse(widget.label.isVisible())
self.assertEqual(
widget.units_combo.currentData(), QgsUnitTypes.DistanceUnit.DistanceMeters
)
widget.setValue(5)
self.assertEqual(widget.getValue(), 5)
widget.units_combo.setCurrentIndex(
widget.units_combo.findData(QgsUnitTypes.DistanceUnit.DistanceKilometers)
)
self.assertEqual(widget.getValue(), 5000)
widget.setValue(2)
self.assertEqual(widget.getValue(), 2000)
widget.setUnitParameterValue(vl.id())
self.assertEqual(widget.getValue(), 2)
widget.setValue(5)
self.assertEqual(widget.getValue(), 5)
widget.deleteLater()
def testMatrix(self):
self.checkConstructWrapper(
QgsProcessingParameterMatrix("test"), FixedTableWidgetWrapper
)
alg = QgsApplication.processingRegistry().createAlgorithmById(
"native:centroids"
)
dlg = AlgorithmDialog(alg)
param = QgsProcessingParameterMatrix(
"test", "test", 2, True, ["x", "y"], [["a", "b"], ["c", "d"]]
)
wrapper = FixedTableWidgetWrapper(param, dlg)
widget = wrapper.createWidget()
# check that default value is initially set
self.assertEqual(wrapper.value(), [["a", "b"], ["c", "d"]])
# test widget
widget.show()
wrapper.setValue([[1, 2], [3, 4]])
self.assertEqual(wrapper.value(), [[1, 2], [3, 4]])
widget.deleteLater()
def testNumber(self):
self.checkConstructWrapper(
QgsProcessingParameterNumber("test"), NumberWidgetWrapper
)
def testBand(self):
self.checkConstructWrapper(
QgsProcessingParameterBand("test"), BandWidgetWrapper
)
if __name__ == "__main__":
unittest.main()