-
Notifications
You must be signed in to change notification settings - Fork 0
/
exprutils.py
157 lines (140 loc) · 5.77 KB
/
exprutils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from abc import ABC
from qgis.core import QgsVectorLayer, QgsExpression, QgsField, QgsFieldProxyModel
from PyQt5.QtCore import Qt, QVariant, QLocale
from PyQt5.QtWidgets import QLineEdit, QCompleter
from PyQt5.QtGui import QDoubleValidator
# operators
OP_CONTAINS = "Contains"
OP_STARTSWITH = "Starts with"
OP_ENDSWITH = "Ends with"
OP_EQUALS = "Equals"
OP_NEQUALS = "Not equals"
OP_LT = "<"
OP_GT = ">"
OP_LTE = "<="
OP_GTE = ">="
OP_EQ = "="
OP_NEQ = "!="
OP_SUBSTR = "Substr"
class ExprUtils(ABC):
@staticmethod
def isFieldNumeric(field: QgsField):
if field.type() == QgsFieldProxyModel.Numeric or field.type() == QgsFieldProxyModel.Int or field.type() == QgsFieldProxyModel.Double or field.type() == QgsFieldProxyModel.LongLong:
return True
else:
return False
@staticmethod
def isFieldString(field: QgsField):
if field.type() == QVariant.String:
return True
else:
return False
@staticmethod
def supportedTypes():
return QgsFieldProxyModel.Numeric | QgsFieldProxyModel.Int | QgsFieldProxyModel.Double | QgsFieldProxyModel.LongLong | QgsFieldProxyModel.String
@staticmethod
def aggrExpr(field: str, useOrderBy: bool = True):
"""This is for the list of feature ids/names/whatever in the result view?"""
#return f'array_distinct("{field}")' # nope, reicht nicht... da kommt nur der wert von einem feature...
if useOrderBy:
return "array_distinct(array_agg(\"{}\", order_by := \"{}\"))".format(field, field)
else:
return "array_distinct(array_agg(\"{}\"))".format(field)
@staticmethod
def listOps(field: QgsField):
if ExprUtils.isFieldNumeric(field):
return [OP_EQ, OP_NEQ, OP_LT, OP_LTE, OP_GT, OP_GTE]
elif ExprUtils.isFieldString(field):
return [OP_CONTAINS, OP_EQUALS, OP_NEQUALS, OP_STARTSWITH, OP_ENDSWITH]
else:
raise Exception()
@staticmethod
def searchExpr(operator: str, field: QgsField):
expr = ""
sens = Qt.CaseInsensitive;
mode = Qt.MatchContains
if operator == OP_STARTSWITH:
mode = Qt.MatchStartsWith
expr = "\"{}\" LIKE '[%{}%]%'"
elif operator == OP_CONTAINS:
expr = "\"{}\" LIKE '%[%{}%]%'"
elif operator == OP_ENDSWITH:
mode = Qt.MatchEndsWith
expr = "\"{}\" LIKE '%[%{}%]'"
elif operator == OP_EQUALS:
mode = Qt.MatchExactly
expr = "\"{}\"='[%{}%]'"
elif operator == OP_NEQUALS:
mode = Qt.MatchExactly
expr = "\"{}\"!='[%{}%]'"
elif operator == OP_EQ:
expr = "\"{}\"=[%{}%]"
elif operator == OP_NEQ:
expr = "\"{}\"!=[%{}%]"
elif operator == OP_LT:
expr = "\"{}\"<[%{}%]"
elif operator == OP_LTE:
expr = "\"{}\"<=[%{}%]"
elif operator == OP_GT:
expr = "\"{}\">[%{}%]"
elif operator == OP_GTE:
expr = "\"{}\">=[%{}%]"
else:
raise Exception()
return (sens, mode, QgsExpression(expr.format(field.name(), field.name())))
@staticmethod
def mapList(field: QgsField, list: list):
"""Convert values in list to str (if not string already)?"""
if field.type() != QgsFieldProxyModel.String:
return map(lambda x: str(x), list)
return list
@staticmethod
def initSearchField(field: QgsField, search: QLineEdit, layer: QgsVectorLayer, locale: QLocale):
"""Prepares the validator (for numeric fields) or the auto completer (for string fields).
For validation a maximum of 1000 match suggestions are provided to ensure performance.
"""
if ExprUtils.isFieldNumeric(field):
qdv = QDoubleValidator()
search.setLocale(locale)
qdv.setLocale(locale)
qdv.setNotation(QDoubleValidator.StandardNotation)
search.setValidator(qdv)
elif ExprUtils.isFieldString(field):
#list = QgsVectorLayerUtils.getValues(layer, aggrExpr) # # array_distinct(array_agg("identifier", order_by := "identifier") -> Tuple[List[Any], bool]
# viel schneller als die expression
field_index = layer.fields().indexOf(f"{field.name()}")
unique_values = sorted(layer.uniqueValues(field_index))
NUM_MAX_HITS = 1000 # TODO make proper "constant" and put it somewhere else
if unique_values:
if len(unique_values) > NUM_MAX_HITS:
pass # not creating an autocompleter
else:
completer = QCompleter(ExprUtils.mapList(field, unique_values))
search.setCompleter(completer)
else:
raise Exception()
@staticmethod
def initSearchExpr(field: QgsField, search: QLineEdit, operator: str):
if ExprUtils.isFieldNumeric(field):
sens, mode, expr = ExprUtils.searchExpr(operator, field)
return expr
elif ExprUtils.isFieldString(field):
sens, mode, expr = ExprUtils.searchExpr(operator, field)
if search.completer() is not None:
search.completer().setCaseSensitivity(sens)
search.completer().setFilterMode(mode)
return expr
else:
raise Exception()
@staticmethod
def value(field: QgsField, search: QLineEdit):
if ExprUtils.isFieldNumeric(field):
doubleVal, isDouble = search.locale().toDouble(search.text())
if isDouble:
return doubleVal
else:
return False
elif ExprUtils.isFieldString(field):
return search.text()
else:
raise Exception()