1# -*- coding: utf-8 -*-
2"""QGIS Unit tests for the attribute table model.
3
4.. note:: This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2 of the License, or
7(at your option) any later version.
8"""
9__author__ = 'Matthias Kuhn'
10__date__ = '27/05/2015'
11__copyright__ = 'Copyright 2015, The QGIS Project'
12
13from qgis.gui import (
14    QgsAttributeTableModel,
15    QgsGui
16)
17from qgis.core import (
18    QgsFeature,
19    QgsGeometry,
20    QgsPointXY,
21    QgsVectorLayer,
22    QgsVectorLayerCache,
23    QgsConditionalStyle,
24)
25from qgis.PyQt.QtCore import Qt
26from qgis.PyQt.QtGui import QColor
27from qgis.testing import (start_app,
28                          unittest
29                          )
30
31start_app()
32
33
34class TestQgsAttributeTableModel(unittest.TestCase):
35
36    @classmethod
37    def setUpClass(cls):
38        QgsGui.editorWidgetRegistry().initEditors()
39
40    def setUp(self):
41        self.layer = self.createLayer()
42        self.cache = QgsVectorLayerCache(self.layer, 100)
43        self.am = QgsAttributeTableModel(self.cache)
44        self.am.loadLayer()
45
46    def tearDown(self):
47        del self.am
48        del self.cache
49        del self.layer
50
51    def createLayer(self):
52        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
53                               "addfeat", "memory")
54        pr = layer.dataProvider()
55        features = list()
56        for i in range(10):
57            f = QgsFeature()
58            f.setAttributes(["test", i])
59            f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100 * i, 2 ^ i)))
60            features.append(f)
61
62        self.assertTrue(pr.addFeatures(features))
63        return layer
64
65    def testLoad(self):
66        self.assertEqual(self.am.rowCount(), 10)
67        self.assertEqual(self.am.columnCount(), 2)
68
69    def testRemove(self):
70        self.layer.startEditing()
71        self.layer.deleteFeature(5)
72        self.assertEqual(self.am.rowCount(), 9)
73        self.layer.selectByIds([1, 3, 6, 7])
74        self.layer.deleteSelectedFeatures()
75        self.assertEqual(self.am.rowCount(), 5)
76
77    def testAdd(self):
78        self.layer.startEditing()
79
80        f = QgsFeature()
81        f.setAttributes(["test", 8])
82        f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200)))
83        self.layer.addFeature(f)
84
85        self.assertEqual(self.am.rowCount(), 11)
86
87    def testRemoveColumns(self):
88        self.assertTrue(self.layer.startEditing())
89
90        self.assertTrue(self.layer.deleteAttribute(1))
91
92        self.assertEqual(self.am.columnCount(), 1)
93
94    def testEdit(self):
95        fid = 2
96        field_idx = 1
97        new_value = 333
98
99        # get the same feature from model and layer
100        feature = self.layer.getFeature(fid)
101        model_index = self.am.idToIndex(fid)
102        feature_model = self.am.feature(model_index)
103
104        # check that feature from layer and model are sync
105        self.assertEqual(feature.attribute(field_idx), feature_model.attribute(field_idx))
106
107        # change attribute value for a feature and commit
108        self.layer.startEditing()
109        self.layer.changeAttributeValue(fid, field_idx, new_value)
110        self.layer.commitChanges()
111
112        # check the feature in layer is good
113        feature = self.layer.getFeature(fid)
114        self.assertEqual(feature.attribute(field_idx), new_value)
115
116        # get the same feature from model and layer
117        model_index = self.am.idToIndex(fid)
118        feature_model = self.am.feature(model_index)
119
120        # check that index from layer and model are sync
121        self.assertEqual(feature.attribute(field_idx), feature_model.attribute(field_idx))
122
123    def testStyle(self):
124        style_threshold = 2
125        color = QColor(133, 133, 133)
126        style = QgsConditionalStyle()
127        style.setRule(f'"fldint" <= {style_threshold}')
128        style.setTextColor(color)
129        self.layer.conditionalStyles().setRowStyles([style])
130
131        for f in self.layer.getFeatures():
132            model_index = self.am.idToIndex(f.id())
133            text_color = self.am.data(model_index, Qt.TextColorRole)
134
135            if f['fldint'] <= style_threshold:
136                self.assertEqual(text_color, color)
137            else:
138                self.assertIsNone(text_color)
139
140        self.assertTrue(self.layer.startEditing())
141
142        feature1 = self.layer.getFeature(2)
143        feature1['fldint'] = style_threshold + 1
144        feature2 = self.layer.getFeature(8)
145        feature2['fldint'] = style_threshold
146
147        self.assertTrue(self.layer.updateFeature(feature1))
148        self.assertTrue(self.layer.updateFeature(feature2))
149        self.assertTrue(self.layer.commitChanges())
150
151        for f in self.layer.getFeatures():
152            model_index = self.am.idToIndex(f.id())
153            text_color = self.am.data(model_index, Qt.TextColorRole)
154
155            if f['fldint'] <= style_threshold:
156                self.assertEqual(color, text_color, f'Feature {f.id()} should have color')
157            else:
158                self.assertIsNone(text_color, f'Feature {f.id()} should have no color')
159
160        self.layer.conditionalStyles().setRowStyles([])
161
162
163if __name__ == '__main__':
164    unittest.main()
165