labeling.py 49.8 KB
Newer Older
1
import typing
Benjamin Jakimow's avatar
Benjamin Jakimow committed
2
import enum
3
import numpy as np
4
import math
5
from qgis.core import QgsMapLayer, QgsRasterLayer, QgsVectorLayer, QgsField, QgsFields, \
6
    QgsEditorWidgetSetup, QgsFeature, QgsVectorLayerTools, QgsFieldModel, \
7
8
9
    QgsRendererCategory, QgsCategorizedSymbolRenderer, QgsProject, QgsMapLayerStore, QgsSymbol
from qgis.gui import QgsDockWidget, QgsSpinBox, QgsDoubleSpinBox, \
    QgsEditorConfigWidget, QgsEditorWidgetFactory, QgsEditorWidgetWrapper, \
10
    QgsGui, QgsEditorWidgetRegistry, \
11
    QgsDateTimeEdit, QgsDateEdit, QgsTimeEdit, QgsActionMenu, QgsAttributeTableModel
12

13
from eotimeseriesviewer.externals.qps.layerproperties import showLayerPropertiesDialog, AttributeTableWidget
14
from eotimeseriesviewer.timeseries import TimeSeriesDate, TimeSeriesSource
Benjamin Jakimow's avatar
Benjamin Jakimow committed
15
from eotimeseriesviewer.vectorlayertools import EOTSVVectorLayerTools
16
from eotimeseriesviewer import DIR_UI
17
18
19
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *
20
from .externals.qps.utils import datetime64, loadUi, SpatialPoint, SpatialExtent
21
22
from .externals.qps.classification.classificationscheme import ClassInfo, ClassificationScheme
from .externals.qps.layerproperties import showLayerPropertiesDialog
Benjamin Jakimow's avatar
Benjamin Jakimow committed
23
from .externals.qps.vectorlayertools import VectorLayerTools
24
25
from .externals.qps.models import Option, OptionListModel
from .externals.qps.classification.classificationscheme import EDITOR_WIDGET_REGISTRY_KEY as CS_KEY
Benjamin Jakimow's avatar
Benjamin Jakimow committed
26

27
# the QgsProject(s) and QgsMapLayerStore(s) to search for QgsVectorLayers
28
29
MAP_LAYER_STORES = [QgsProject.instance()]

30
31
32
EDITOR_WIDGET_REGISTRY_KEY = 'EOTSV Quick Label'


Benjamin Jakimow's avatar
Benjamin Jakimow committed
33
class LabelConfigurationKey(object):
34
35
36
    ClassificationScheme = 'classificationScheme'
    LabelType = 'labelType'
    LabelGroup = 'labelGroup'
37

Benjamin Jakimow's avatar
Benjamin Jakimow committed
38

39
class LabelShortcutType(enum.Enum):
40
41
    """Enumeration for shortcuts to be derived from a TimeSeriesDate instance"""
    Off = 'No Quick Label (default)'
42
43
44
    Date = 'Date'
    DateTime = 'Date-Time'
    Time = 'Time'
Benjamin Jakimow's avatar
Benjamin Jakimow committed
45
46
    DOY = 'Day of Year (DOY)'
    Year = 'Year'
47
48
    DecimalYear = 'Decimal Year'
    Sensor = 'Sensor Name'
49
    SourceImage = 'Source Image'
50

Benjamin Jakimow's avatar
Benjamin Jakimow committed
51
    # Classification = 'Classification'
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    @staticmethod
    def fromConfValue(value: str):
        for t in LabelShortcutType:
            if value in [t.name, t.value]:
                return t
        return LabelShortcutType.Off

    def confValue(self) -> str:
        return self.name

    def text(self) -> str:
        return self.value

    def __str__(self):
        return str(self.name)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
69

70
def shortcuts(field: QgsField) -> typing.List[LabelShortcutType]:
71
72
    """
    Returns the possible LabelShortCutTypes for a certain field
73
74
    :param field:
    :type field:
75
76
77
78
79
    :param fieldName: str
    :return: [list]
    """
    assert isinstance(field, QgsField)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
80
    shortCutsString = [LabelShortcutType.Sensor, LabelShortcutType.Date, LabelShortcutType.Time,
81
                       LabelShortcutType.DateTime, LabelShortcutType.SourceImage]
82
83
    shortCutsInt = [LabelShortcutType.Year, LabelShortcutType.DOY]
    shortCutsFloat = [LabelShortcutType.Year, LabelShortcutType.DOY, LabelShortcutType.DecimalYear]
84

85
    options = [LabelShortcutType.Off]
86
87
    fieldType = field.type()
    if fieldType in [QVariant.String]:
88
89
90
        options.extend(shortCutsString)
        options.extend(shortCutsInt)
        options.extend(shortCutsFloat)
91
    elif fieldType in [QVariant.Int, QVariant.LongLong, QVariant.UInt, QVariant.ULongLong]:
92
        options.extend(shortCutsInt)
93
    elif fieldType in [QVariant.Double]:
94
95
        options.extend(shortCutsInt)
        options.extend(shortCutsFloat)
96
97
98
99
100
101
    elif fieldType == QVariant.DateTime:
        options.extend([LabelShortcutType.DateTime])
    elif fieldType == QVariant.Date:
        options.extend([LabelShortcutType.Date])
    elif fieldType == QVariant.Time:
        options.extend([LabelShortcutType.Time])
102
103
    else:
        s = ""
104
105
106
107
108
    result = []
    for o in options:
        if o not in result:
            result.append(o)
    return result
109

110

Benjamin Jakimow's avatar
Benjamin Jakimow committed
111
def layerClassSchemes(layer: QgsVectorLayer) -> typing.List[ClassificationScheme]:
112
113
114
115
116
    """
    Returns a list of (ClassificationScheme, QgsField) for all QgsFields with QgsEditorWidget being QgsClassificationWidgetWrapper or RasterClassification.
    :param layer: QgsVectorLayer
    :return: list [(ClassificationScheme, QgsField), ...]
    """
117
    assert isinstance(layer, QgsVectorLayer)
118
119
    from .externals.qps.classification.classificationscheme import EDITOR_WIDGET_REGISTRY_KEY as CS_KEY
    from .externals.qps.classification.classificationscheme import classSchemeFromConfig
120
121
122
    schemes = []
    for i in range(layer.fields().count()):
        setup = layer.editorWidgetSetup(i)
123
124
        field = layer.fields().at(i)
        assert isinstance(field, QgsField)
125
        assert isinstance(setup, QgsEditorWidgetSetup)
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

        if setup.type() == CS_KEY:
            cs = classSchemeFromConfig(setup.config())
            if isinstance(cs, ClassificationScheme) and len(cs) > 0:
                schemes.append((cs, field))

        elif setup.type() == 'Classification' and isinstance(layer.renderer(), QgsCategorizedSymbolRenderer):
            renderer = layer.renderer()
            cs = ClassificationScheme()
            for l, cat in enumerate(renderer.categories()):
                assert isinstance(cat, QgsRendererCategory)
                symbol = cat.symbol()
                assert isinstance(symbol, QgsSymbol)
                cs.insertClass(ClassInfo(l, name=cat.value(), color=symbol.color()))
            if len(cs) > 0:
                schemes.append((cs, field))
142
143
    return schemes

144

145
def labelShortcutLayerClassificationSchemes(layer: QgsVectorLayer):
146
    """
147
    Returns the ClassificationSchemes + QgsField used for labeling shortcuts
148
    :param layer: QgsVectorLayer
149
    :return: [(ClassificationScheme, QgsField), (ClassificationScheme, QgsField), ...]
150
151
152
153
154
155
156
157
    """
    classSchemes = []
    assert isinstance(layer, QgsVectorLayer)
    for i in range(layer.fields().count()):
        setup = layer.editorWidgetSetup(i)
        assert isinstance(setup, QgsEditorWidgetSetup)
        if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
            conf = setup.config()
158
            ci = conf.get(LabelConfigurationKey.ClassificationScheme.value)
159
            if isinstance(ci, ClassificationScheme) and ci not in classSchemes:
160
                classSchemes.append((ci, layer.fields().at(i)))
161
162

    return classSchemes
163

164

Benjamin Jakimow's avatar
Benjamin Jakimow committed
165
def quickLabelLayers() -> typing.List[QgsVectorLayer]:
166
    """
167
    Returns a list of known QgsVectorLayers with at least one LabelShortcutEditWidget
168
169
    :return: [list-of-QgsVectorLayer]
    """
170
    layers = []
171

172
173
174
175
176
177
178
179
    classSchemes = set()
    for store in MAP_LAYER_STORES:
        assert isinstance(store, (QgsProject, QgsMapLayerStore))
        for layer in store.mapLayers().values():
            if isinstance(layer, QgsVectorLayer):
                for i in range(layer.fields().count()):
                    setup = layer.editorWidgetSetup(i)
                    assert isinstance(setup, QgsEditorWidgetSetup)
180
                    if setup.type() in [EDITOR_WIDGET_REGISTRY_KEY, CS_KEY, 'Classification']:
181
182
                        if layer not in layers:
                            layers.append(layer)
183
                        break
184
    return layers
185

186

187
188
def quickLayerGroups(layer) -> typing.List[str]:
    groups = set()
189
190
    if isinstance(layer, list):
        for l in layer:
191
            groups.update(quickLayerGroups(l))
192
    else:
193
194
195
196
197
        for i, field in enumerate(layer.fields()):
            setup = layer.editorWidgetSetup(i)
            if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
                groups.add(setup.config().get(LabelConfigurationKey.LabelGroup, ''))
    return sorted(groups)
198
199


200
201
202
203
204
205
206
207
208
209
210
211
212
213
def quickLayerFieldSetup(layer, label_group: str = None) -> typing.List[QgsField]:
    fields = []
    if isinstance(layer, list):
        for l in layer:
            fields.extend(quickLayerFieldSetup(l))
    else:
        for i, field in enumerate(layer.fields()):
            setup = layer.editorWidgetSetup(i)
            if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
                if isinstance(label_group, str):
                    grp = setup.config().get(LabelConfigurationKey.LabelGroup, '')
                    if grp != label_group:
                        continue
                fields.append(field)
214
215
216
    return fields


217
218
219
def setQuickTSDLabelsForRegisteredLayers(tsd: TimeSeriesDate,
                                         tss: TimeSeriesSource,
                                         layer_group: str = ''):
220
    """
221
    :param tsd: TimeSeriesDate
222
223
    :param classInfos:
    """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
224
    for layer in quickLabelLayers():
225
        assert isinstance(layer, QgsVectorLayer)
226
        if layer.isEditable():
227
            setQuickTSDLabels(layer, tsd, tss, label_group=layer_group)
228

229
230

def setQuickClassInfo(vectorLayer: QgsVectorLayer, field, classInfo: ClassInfo):
231
232
233
234
235
236
237
    """
    Sets the ClassInfo value or label to selected features
    :param vectorLayer: QgsVectorLayer
    :param field: QgsField or int with field index
    :param classInfo: ClassInfo
    """
    assert isinstance(vectorLayer, QgsVectorLayer)
238

239
    assert isinstance(classInfo, ClassInfo)
240

241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
    if isinstance(field, QgsField):
        idx = vectorLayer.fields().lookupField(field.name())
    else:
        idx = field

    field = vectorLayer.fields().at(idx)

    vectorLayer.beginEditCommand('Set class info "{}"'.format(classInfo.name()))

    if field.type() == QVariant.String:
        value = str(classInfo.name())
    else:
        value = classInfo.label()

    for feature in vectorLayer.selectedFeatures():
        assert isinstance(feature, QgsFeature)
        oldValue = feature.attribute(field.name())
        vectorLayer.changeAttributeValue(feature.id(), idx, value, oldValue)
    vectorLayer.endEditCommand()

261

262
263
264
265
def setQuickTSDLabels(vectorLayer: QgsVectorLayer,
                      tsd: TimeSeriesDate,
                      tss: TimeSeriesSource,
                      label_group: str = ''):
266
    """
267
    Labels selected features with information related to TimeSeriesDate tsd, according to
268
    the settings specified in this model. Note: this will not the any ClassInfo or the source image values
269
    :param tsd: TimeSeriesDate
270
271
    :param classInfos:
    """
272
    assert isinstance(tsd, TimeSeriesDate)
273
    assert isinstance(vectorLayer, QgsVectorLayer)
274
275
    if not vectorLayer.isEditable():
        return
276
    vectorLayer.beginEditCommand('Quick labels {}'.format(tsd.date()))
277
278
279
280
281
    for field in quickLayerFieldSetup(vectorLayer, label_group=label_group):
        assert isinstance(field, QgsField)
        iField: int = vectorLayer.fields().lookupField(field.name())
        assert iField >= 0
        setup: QgsEditorWidgetSetup = vectorLayer.editorWidgetSetup(iField)
282
        assert isinstance(setup, QgsEditorWidgetSetup)
283
        assert setup.type() == EDITOR_WIDGET_REGISTRY_KEY
284

285
286
287
        fieldType = field.type()
        conf = setup.config()
        labelType: LabelShortcutType = LabelShortcutType.fromConfValue(conf.get(LabelConfigurationKey.LabelType))
288

Benjamin Jakimow's avatar
Benjamin Jakimow committed
289
        value = quickLabelValue(fieldType, labelType, tsd, tss)
290

291
292
293
294
295
        if value is not None:
            for feature in vectorLayer.selectedFeatures():
                assert isinstance(feature, QgsFeature)
                oldValue = feature.attribute(field.name())
                vectorLayer.changeAttributeValue(feature.id(), iField, value, oldValue)
296

297
        vectorLayer.endEditCommand()
298
        vectorLayer.triggerRepaint()
299
300
301
    pass


Benjamin Jakimow's avatar
Benjamin Jakimow committed
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
def quickLabelValue(fieldType: QVariant,
                    labelType: LabelShortcutType,
                    tsd: TimeSeriesDate,
                    tss: TimeSeriesSource):
    value = None
    datetime: QDateTime = QDateTime(tsd.date().astype(object))

    if labelType == LabelShortcutType.Off:
        return value

    if labelType == LabelShortcutType.Sensor:
        if fieldType == QVariant.String:
            value = tsd.sensor().name()

    elif labelType == LabelShortcutType.DOY:
        if fieldType in [QVariant.Double, QVariant.Int]:
            value = tsd.doy()
        elif fieldType == QVariant.String:
            value = str(tsd.doy())

    elif labelType == LabelShortcutType.Date:
        if fieldType == QVariant.Date:
            value = datetime.date()
        elif fieldType == QVariant.DateTime:
            value = QDateTime(datetime.date())
        elif fieldType == QVariant.String:
            value = datetime.date().toPyDate().isoformat()

    elif labelType == LabelShortcutType.DateTime:
        if fieldType == QVariant.Date:
            value = datetime.date()
        elif fieldType == QVariant.DateTime:
            value = datetime
        elif fieldType == QVariant.String:
            value = datetime.toPyDateTime().isoformat()

    elif labelType == LabelShortcutType.Time:
        if fieldType == QVariant.Date:
            value = None
        elif fieldType == QVariant.DateTime:
            value = datetime
        elif fieldType == QVariant.Time:
            value = datetime.time()
        elif fieldType == QVariant.String:
            value = datetime.time().toPyTime().isoformat()

    elif labelType == LabelShortcutType.Year:

        if fieldType == QVariant.String:
            value = str(datetime.date().year())
        elif fieldType == QVariant.Date:
            value = datetime.date()
        elif fieldType == QVariant.DateTime:
            value = datetime
        elif fieldType == QVariant.Time:
            value = datetime.time()
        elif fieldType == QVariant.Int:
            value = datetime.date().year()

    elif labelType == LabelShortcutType.DecimalYear:
        if fieldType == QVariant.String:
            value = str(tsd.decimalYear())
        elif fieldType == QVariant.Int:
            value = int(tsd.decimalYear())
        elif fieldType == QVariant.Double:
            value = tsd.decimalYear()

    elif labelType == LabelShortcutType.SourceImage and isinstance(tss, TimeSeriesSource):
        if fieldType == QVariant.String:
            value = tss.uri()

    if value is not None and fieldType == QVariant.String:
        value = str(value)
    return value


378
379
380
381
382
383
384
385
class LabelAttributeTableModel(QAbstractTableModel):

    def __init__(self, parent=None, *args):

        super(LabelAttributeTableModel, self).__init__()

        self.cnField = 'Field'
        self.cnFieldType = 'Type'
386
        self.cnLabel = 'Label shortcut'
387
        self.mColumnNames = [self.cnField, self.cnFieldType, self.cnLabel]
388
        # self.mLabelTypes = dict()
389
390
        self.mVectorLayer = None

391
    def setVectorLayer(self, layer: QgsVectorLayer):
392
393
394
395

        if isinstance(layer, QgsVectorLayer):
            layer.attributeAdded.connect(self.resetModel)
            layer.attributeDeleted.connect(self.resetModel)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
396

397
398
399
400
            self.mVectorLayer = layer
        else:
            self.mVectorLayer = None

401
        self.resetModel()
402

Benjamin Jakimow's avatar
Benjamin Jakimow committed
403
    def hasVectorLayer(self) -> bool:
404
405
406
407
        """
        Returns true if a QgsVectorLayer is specified.
        :return: bool
        """
408
409
410
411
        return isinstance(self.mVectorLayer, QgsVectorLayer)

    def resetModel(self):
        self.beginResetModel()
412

413
414
415
416
417
418
        if isinstance(self.mVectorLayer, QgsVectorLayer):
            fields = self.mVectorLayer.fields()
            assert isinstance(fields, QgsFields)
            for i in range(fields.count()):
                field = fields.at(i)
                assert isinstance(field, QgsField)
419
                # self.mLabelTypes[field.name()] = LabelShortcutType.Off
420
421
422

        self.endResetModel()

423
    def rowCount(self, parent=QModelIndex()) -> int:
424
425
426
427
428
        if isinstance(self.mVectorLayer, QgsVectorLayer):
            return self.mVectorLayer.fields().count()
        else:
            return 0

429
    def fieldName2Index(self, fieldName: str) -> str:
430
        assert isinstance(fieldName, str)
431
432
433
434

        if isinstance(self.mVectorLayer, QgsVectorLayer):
            fields = self.mVectorLayer.fields()
            assert isinstance(fields, QgsFields)
435
            i = fields.indexOf(fieldName)
436
437
438
439
            return self.createIndex(i, 0)
        else:
            return QModelIndex()

440
    def field2index(self, field: QgsField) -> QModelIndex:
441
442
443
        assert isinstance(field, QgsField)
        return self.fieldName2Index(field.name())

444
    def index2editorSetup(self, index: QModelIndex):
445
446
447
448
449
        if index.isValid() and isinstance(self.mVectorLayer, QgsVectorLayer):
            return self.mVectorLayer.editorWidgetSetup(index.row())
        else:
            return None

450
    def index2field(self, index: QModelIndex) -> QgsField:
451
452
453
454
455
456
457
        if index.isValid() and isinstance(self.mVectorLayer, QgsVectorLayer):
            fields = self.mVectorLayer.fields()
            assert isinstance(fields, QgsFields)
            return fields.at(index.row())
        else:
            return None

458
    def columnCount(self, parent=QModelIndex()) -> int:
459
460
        return len(self.mColumnNames)

461
    def setFieldShortCut(self, fieldName: str, attributeType: LabelShortcutType):
462
463
464
        if isinstance(fieldName, QgsField):
            fieldName = fieldName.name()
        assert isinstance(fieldName, str)
465
        assert isinstance(attributeType, LabelShortcutType)
466
467
468
469
470
471
472
473
474
475
476

        if self.hasVectorLayer():
            fields = self.mVectorLayer.fields()
            assert isinstance(fields, QgsFields)
            i = self.mVectorLayer.fields().indexFromName(fieldName)
            assert i >= 0
            field = self.mVectorLayer.fields().at(i)
            idx = self.field2index(field)

            self.setData(self.createIndex(idx.row(), 2), attributeType, role=Qt.EditRole)

477
    def shortcuts(self, field: QgsField):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
        """
        Returns the possible LabelShortCutTypes for a certain field
        :param fieldName: str
        :return: [list]
        """
        if not self.hasVectorLayer():
            return []

        if isinstance(field, QModelIndex):
            field = self.index2field(field)

        if isinstance(field, str):
            i = self.mVectorLayer.fields().lookupField(field)
            field = self.mVectorLayer.fields().at(i)

        assert isinstance(field, QgsField)
494
        return shortcuts(field)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
495

496
    def data(self, index, role=Qt.DisplayRole):
497
498
499
500
501
        if role is None or not index.isValid():
            return None

        value = None
        columnName = self.mColumnNames[index.column()]
502
503
504
        fields = self.mVectorLayer.fields()
        assert isinstance(fields, QgsFields)
        field = fields.at(index.row())
505
        assert isinstance(field, QgsField)
506
507
        setup = self.mVectorLayer.editorWidgetSetup(index.row())
        assert isinstance(setup, QgsEditorWidgetSetup)
508
509
510
511
512
513
514

        if role == Qt.DisplayRole or role == Qt.ToolTipRole:
            if columnName == self.cnField:
                value = field.name()
            elif columnName == self.cnFieldType:
                value = '{}'.format(field.typeName())
            elif columnName == self.cnLabel:
515
516
                fac = QgsGui.editorWidgetRegistry().factory(setup.type())
                value = fac.name()
517
518
            else:
                s = ""
519
        elif role == Qt.UserRole:
520
            value = setup
521
522
523
524
525
526
527
        return value

    def setData(self, index, value, role=None):
        if role is None or not index.isValid():
            return None

        columnName = self.mColumnNames[index.column()]
528
529
530
        fields = self.mVectorLayer.fields()
        assert isinstance(fields, QgsFields)
        field = fields.at(index.row())
531
        assert isinstance(field, QgsField)
532
533
534
        setup = self.mVectorLayer.editorWidgetSetup(index.row())
        assert isinstance(setup, QgsEditorWidgetSetup)

535
536
        changed = False
        if columnName == self.cnLabel and role == Qt.EditRole:
537
            if isinstance(value, str):
538
                setup = QgsEditorWidgetSetup(value, {})
539
540
                self.mVectorLayer.setEditorWidgetSetup(index.row(), setup)

541
542
                changed = True
        if changed:
543
            self.dataChanged.emit(index, index, [role])
544
545
        return changed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
546
    def columnName(self, index: int) -> str:
547
548
549
550
551
552
        if isinstance(index, QModelIndex):
            if not index.isValid():
                return None
            index = index.column()
        return self.mColumnNames[index]

553
554
555
    def flags(self, index):
        if index.isValid():
            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
556
557
            if self.columnName(index) == self.cnLabel:
                flags = flags | Qt.ItemIsEditable
558
559
560
561
562
563
564
565
566
567
568
569
570
            return flags
        return None

    def headerData(self, col, orientation, role):
        if Qt is None:
            return None
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.mColumnNames[col]
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return col
        return None


571
572
573
574
575
576
577
578
579
580
581
582
583
584
class LabelAttributeTypeWidgetDelegate(QStyledItemDelegate):
    """

    """

    def __init__(self, tableView: QTableView, labelAttributeTableModel: LabelAttributeTableModel, parent=None):
        super(LabelAttributeTypeWidgetDelegate, self).__init__(parent=parent)
        assert isinstance(tableView, QTableView)
        assert isinstance(labelAttributeTableModel, LabelAttributeTableModel)

        self.mTableView = tableView
        self.mLabelAttributeTableModel = labelAttributeTableModel
        self.setItemDelegates(tableView)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
585
    def model(self) -> LabelAttributeTableModel:
586
587
588
589
590
591
592
593
594
        return self.mTableView.model()

    def setItemDelegates(self, tableView):
        assert isinstance(tableView, QTableView)
        model = self.model()
        for c in [model.cnLabel]:
            i = model.mColumnNames.index(c)
            tableView.setItemDelegateForColumn(i, self)

595
    def columnName(self, index: QModelIndex) -> str:
596
597
598
599
600
601
        if not index.isValid():
            return None
        return self.model().mColumnNames[index.column()]

    def createEditor(self, parent, option, index):
        cname = self.columnName(index)
602
603
604
        model = self.model()
        layer = model.mVectorLayer
        idx = index.row()
605
606
607
608
        w = None
        if index.isValid() and isinstance(model, LabelAttributeTableModel):
            if cname == model.cnLabel:
                w = QComboBox(parent=parent)
609
610
611
612
613
614
615
616
                reg = QgsGui.editorWidgetRegistry()
                assert isinstance(reg, QgsEditorWidgetRegistry)
                factories = reg.factories()
                i = 0
                for key, fac in reg.factories().items():
                    score = fac.fieldScore(layer, idx)
                    if score > 0:
                        w.addItem(fac.name(), key)
617
618
619
620
        return w

    def setEditorData(self, editor, index):
        cname = self.columnName(index)
621
622
        model = self.model()
        layer = model.mVectorLayer
623
624
625
626

        w = None
        if index.isValid() and isinstance(model, LabelAttributeTableModel):
            if cname == model.cnLabel and isinstance(editor, QComboBox):
627
628
                key = model.data(index, role=Qt.UserRole)
                i = -1
629
                for i in range(editor.count()):
630
                    if editor.itemData(i, role=Qt.UserRole) == key:
631
632
633
634
                        editor.setCurrentIndex(i)
                        break

    def setModelData(self, w, model, index):
635
636
        assert isinstance(model, LabelAttributeTableModel)
        assert isinstance(index, QModelIndex)
637
638
639
640

        cname = model.columnName(index)
        if index.isValid() and isinstance(model, LabelAttributeTableModel):
            if cname == model.cnLabel and isinstance(w, QComboBox):
641
                model.setData(index, w.currentData(Qt.UserRole), Qt.EditRole)
642

Benjamin Jakimow's avatar
Benjamin Jakimow committed
643

Benjamin Jakimow's avatar
Benjamin Jakimow committed
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
class GotoFeatureOptions(enum.IntFlag):
    SelectFeature = 1
    PanToFeature = 2
    ZoomToFeature = 4
    FocusVisibility=8


def gotoFeature(fid: int, layer: QgsVectorLayer, tools: EOTSVVectorLayerTools, options: GotoFeatureOptions):
    if GotoFeatureOptions.SelectFeature in options:
        layer.selectByIds([fid])
    if GotoFeatureOptions.PanToFeature in options:
        tools.panToSelected(layer)
    if GotoFeatureOptions.ZoomToFeature in options:
        tools.zoomToSelected(layer)
    if GotoFeatureOptions.FocusVisibility in options:
        tools.focusVisibility()


def gotoNextFeature(layer: typing.Union[QgsVectorLayer, AttributeTableWidget],
                    tools: EOTSVVectorLayerTools = None,
                    visible_features: typing.List[int] = None,
                    options: GotoFeatureOptions = GotoFeatureOptions.SelectFeature
                    ) -> int:

    if isinstance(layer, AttributeTableWidget):
        return gotoNextFeature(layer.mLayer, layer.vectorLayerTools(), layer.mMainView.filteredFeatures(), options)

671
672
    assert isinstance(tools, QgsVectorLayerTools)
    if isinstance(layer, QgsVectorLayer) and layer.hasFeatures():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
673
674
675
676
677
678
679
680
681
        if not isinstance(visible_features, list):
            visible_features = layer.allFeatureIds()

        if len(visible_features) == 0:
            return None

        visible_features = sorted(visible_features)
        selected_fids = layer.selectedFeatureIds()
        selected_fids = [f for f in visible_features if f in selected_fids]
682

Benjamin Jakimow's avatar
Benjamin Jakimow committed
683
684
        if len(selected_fids) == 0:
            nextFID = visible_features[0]
685
        else:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
686
687
688
689
690
691
            next_index = visible_features.index(max(selected_fids)) + 1
            if next_index >= len(visible_features):
                nextFID = visible_features[-1]
            else:
                nextFID = visible_features[next_index]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
692
        gotoFeature(nextFID, layer, tools, options)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
693
694
        return nextFID
    return None
695
696


Benjamin Jakimow's avatar
Benjamin Jakimow committed
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
def gotoPreviousFeature(layer: typing.Union[QgsVectorLayer, AttributeTableWidget],
                        tools: EOTSVVectorLayerTools = None,
                        visible_features: typing.List[int] = None,
                        options: GotoFeatureOptions = GotoFeatureOptions.SelectFeature):
    """
    Selects the next feature
    :param layer:
    :type layer:
    :param tools:
    :type tools:
    :param visible_features:
    :type visible_features:
    :return:
    :rtype:
    """
    if isinstance(layer, AttributeTableWidget):
        return gotoPreviousFeature(layer.mLayer, layer.vectorLayerTools(), layer.mMainView.filteredFeatures(), options)

715
716
    assert isinstance(tools, QgsVectorLayerTools)
    if isinstance(layer, QgsVectorLayer) and layer.hasFeatures():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
717
718
719
720
721
722
723
724
725
726
727
728
        if not isinstance(visible_features, list):
            visible_features = layer.allFeatureIds()

        if len(visible_features) == 0:
            return None

        visible_features = sorted(visible_features)
        selected_fids = layer.selectedFeatureIds()
        selected_fids = [f for f in visible_features if f in selected_fids]

        if len(selected_fids) == 0:
            nextFID = visible_features[0]
729
        else:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
730
731
732
733
734
735
            next_index = visible_features.index(max(selected_fids)) - 1
            if next_index < 0:
                nextFID = visible_features[0]
            else:
                nextFID = visible_features[next_index]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
736
        gotoFeature(nextFID, layer, tools, options)
737
738
739
        s = ""
        return nextFID
    return None
740
741


Benjamin Jakimow's avatar
Benjamin Jakimow committed
742
class LabelWidget(AttributeTableWidget):
743
744
745
    sigMoveTo = pyqtSignal([QDateTime],
                           [QDateTime, object])

746
747
748
749
    def __init__(self, *args, **kwds):

        super().__init__(*args, *kwds)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
750
        self.mActionNextFeature: QAction = QAction('Next Feature', parent=self)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
751
        self.mActionNextFeature.setIcon(QIcon(':/images/themes/default/mActionAtlasNext.svg'))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
752
753
754
        self.mActionNextFeature.triggered.connect(self.onGotoNextFeature)

        self.mActionPreviousFeature: QAction = QAction('Previous Feature', parent=self)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
755
        self.mActionPreviousFeature.setIcon(QIcon(':/images/themes/default/mActionAtlasPrev.svg'))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
756
        self.mActionPreviousFeature.triggered.connect(self.onGotoPreviousFeature)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
757
758

        m = QMenu()
759
        m.setToolTip('Optional actions after clicking the next / previous feature button.')
760
        m.setToolTipsVisible(True)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
761
762
763
764
765

        self.mOptionAutoSelectNextFeature = m.addAction('Auto select')
        self.mOptionAutoSelectNextFeature.setToolTip('Automatically selects the next / previous feature')
        self.mOptionAutoSelectNextFeature.setCheckable(True)
        self.mOptionAutoSelectNextFeature.setIcon(QIcon(':/images/themes/default/mIconSelected.svg'))
766
767
        self.mOptionAutoSelectNextFeature.setChecked(True)
        self.mOptionAutoSelectNextFeature.setVisible(False)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
768
769
770
771
772
773
774
775

        self.mOptionAutoPan = m.addAction('Auto pan')
        self.mOptionAutoPan.setToolTip('Automatically pans the the next / previous feature')
        self.mOptionAutoPan.setIcon(QIcon(':/images/themes/default/mActionPanToSelected.svg'))
        self.mOptionAutoPan.setCheckable(True)

        self.mOptionAutoUpdateImageVisibility = m.addAction('Auto visibility update')
        self.mOptionAutoUpdateImageVisibility.setToolTip(
776
            r'Automatically shows/hides dates that do/don\'t intersect with spatial map extent.')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
777
778
        self.mOptionAutoUpdateImageVisibility.setCheckable(True)
        self.mOptionAutoUpdateImageVisibility.setIcon(QIcon(':/eotimeseriesviewer/icons/mapview.svg'))
779

Benjamin Jakimow's avatar
Benjamin Jakimow committed
780
        self.mActionNextFeature.setMenu(m)
781
        # self.mActionPreviousFeature.setMenu(m)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
782
783
784
785
786
787
788
789

        m = QMenu()
        m.setToolTipsVisible(True)

        self.mOptionSelectBehaviour: QAction = m.addAction('Selection behaviour')
        self.mOptionSelectBehaviour.setCheckable(True)
        self.mOptionSelectBehaviour.setChecked(True)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
790
791
792
793
794
795
796
797
        self.mOptionSelectionSetSelection = m.addAction('Set Selection')
        self.mOptionSelectionSetSelection.setIcon(QIcon(':/images/themes/default/mIconSelected.svg'))
        self.mOptionSelectionSetSelection.setToolTip('Selects a feature.')

        self.mOptionSelectionAddToSelection = m.addAction('Add to Selection')
        self.mOptionSelectionAddToSelection.setIcon(QIcon(':/images/themes/default/mIconSelectAdd.svg'))
        self.mOptionSelectionAddToSelection.setToolTip('Adds a new feature to an existing selection.')

Benjamin Jakimow's avatar
Benjamin Jakimow committed
798
799
        # self.mOptionSelectionIntersectSelection = m.addAction('Intersect Selection')
        # self.mOptionSelectionIntersectSelection.setIcon(QIcon(':/images/themes/default/mIconSelectIntersect.svg'))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
800

Benjamin Jakimow's avatar
Benjamin Jakimow committed
801
802
        # self.mOptionRemoveFromSelection = m.addAction('Remove from Selection')
        # self.mOptionRemoveFromSelection.setIcon(QIcon(':/images/themes/default/mIconSelectRemove.svg'))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
803
804
805
806
807

        self.mOptionSelectBehaviour.setMenu(m)

        for o in [self.mOptionSelectionSetSelection,
                  self.mOptionSelectionAddToSelection,
Benjamin Jakimow's avatar
Benjamin Jakimow committed
808
809
                  # self.mOptionSelectionIntersectSelection,
                  # self.mOptionRemoveFromSelection
Benjamin Jakimow's avatar
Benjamin Jakimow committed
810
811
812
813
                  ]:
            o.setCheckable(True)
            o.triggered.connect(self.onSelectBehaviourOptionTriggered)

814
        self.mOptionSelectionSetSelection.trigger()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
815
        # show selected feature on top by default
Benjamin Jakimow's avatar
Benjamin Jakimow committed
816
        # self.mActionSelectedToTop.setChecked(True)
817

Benjamin Jakimow's avatar
Benjamin Jakimow committed
818
        self.mToolbar: QToolBar
819
820
821
        self.mToolbar.insertActions(self.mActionToggleEditing,
                                    [self.mActionPreviousFeature,
                                     self.mActionNextFeature,
Benjamin Jakimow's avatar
Benjamin Jakimow committed
822
                                     self.mOptionSelectBehaviour])
823

Benjamin Jakimow's avatar
Benjamin Jakimow committed
824
825
        self.mToolbar.insertSeparator(self.mActionToggleEditing)

826
827
828
829
830
831
832
833
834
835
836
837
838
        self.actionShowProperties = QAction('Show Layer Properties')
        self.actionShowProperties.setToolTip('Show Layer Properties')
        self.actionShowProperties.setIcon(QIcon(':/images/themes/default/propertyicons/system.svg'))
        self.actionShowProperties.triggered.connect(self.showProperties)

        self.btnShowProperties = QToolButton()
        self.btnShowProperties.setAutoRaise(True)
        self.btnShowProperties.setDefaultAction(self.actionShowProperties)

        self.centerBottomLayout.insertWidget(self.centerBottomLayout.indexOf(self.mAttributeViewButton),
                                             self.btnShowProperties)

        self.mLayer.featureAdded.connect(self.onLabelFeatureAdded)
839
840
        self.mMainView.tableView().willShowContextMenu.connect(self.onShowContextMenu)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
841
842
    def onGotoNextFeature(self, *arg):
        # todo: allow to got to unselected features. needs VectorLayerTools support panToFids / zoomToFids
843
844
845
        fid = gotoNextFeature(self, options=self.gotoFeatureOptions())
        if isinstance(fid, int):
            self.mMainView.tableView().scrollToFeature(fid)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862

    def gotoFeatureOptions(self) -> GotoFeatureOptions:
        """
        Returns the GoTo-Feature options
        :return:
        :rtype:
        """
        options = GotoFeatureOptions(0)
        if self.mOptionAutoSelectNextFeature.isChecked():
            options = options | GotoFeatureOptions.SelectFeature
        if self.mOptionAutoPan.isChecked():
            options = options | GotoFeatureOptions.PanToFeature
        if self.mOptionAutoUpdateImageVisibility.isChecked():
            options = options | GotoFeatureOptions.FocusVisibility
        return options

    def onGotoPreviousFeature(self, *args):
863
        fid = gotoPreviousFeature(self, options=self.gotoFeatureOptions())
864
865
        if isinstance(fid, int):
            self.mMainView.tableView().scrollToFeature(fid)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
866

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
    def onShowContextMenu(self, menu: QMenu, idx: QModelIndex):

        fid = idx.data(QgsAttributeTableModel.FeatureIdRole)
        fieldIdx = idx.data(QgsAttributeTableModel.FieldIndexRole)
        lyr = self.mLayer
        if not isinstance(lyr, QgsVectorLayer):
            return

        feature: QgsFeature = self.mLayer.getFeature(fid)
        if not isinstance(feature, QgsFeature):
            return

        ACTIONS = dict()
        for a in menu.findChildren(QAction):
            ACTIONS[a.text()] = a

        # todo: connect default options

        extent: SpatialExtent = SpatialExtent(lyr.crs(), feature.geometry().boundingBox())
        center: SpatialPoint = SpatialPoint(lyr.crs(), feature.geometry().centroid().asPoint())
        field = lyr.fields().at(fieldIdx)

        if field.type() in [QVariant.Date, QVariant.DateTime]:

            if isinstance(feature, QgsFeature):
                datetime = feature.attribute(fieldIdx)
                try:
                    datetime = QDateTime(datetime64(datetime).astype(object))
                except:
                    pass

                # add temporal options
                if isinstance(datetime, QDateTime):
                    date_string = datetime.toString(Qt.ISODate)
                    a1 = QAction(f'Move time to', menu)
                    a1.setToolTip(f'Moves the current date to {date_string}')
                    a1.triggered.connect(lambda *args, d=datetime:
                                         self.sigMoveTo[QDateTime].emit(d))

                    a2 = QAction(f'Move time && pan to', menu)
                    a2.setToolTip(f'Moves the current date to {date_string} and pans to feature {feature.id()}')
                    a2.triggered.connect(lambda *args, f=feature, d=datetime:
                                         self.sigMoveTo[QDateTime, object].emit(d, center))

                    a3 = QAction(f'Move time && zoom to', menu)
                    a2.setToolTip(f'Moves the current date to {date_string} and zooms to feature {feature.id()}')
                    a3.triggered.connect(lambda *args, f=feature, d=datetime:
                                         self.sigMoveTo[QDateTime, object].emit(d, extent))

                    menu.insertActions(menu.actions()[0], [a1, a2, a3])
                    menu.insertSeparator(menu.actions()[3])

Benjamin Jakimow's avatar
Benjamin Jakimow committed
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
    def selectBehaviour(self) -> QgsVectorLayer.SelectBehavior:
        if self.mOptionSelectionSetSelection.isChecked():
            return QgsVectorLayer.SetSelection
        elif self.mOptionSelectionAddToSelection.isChecked():
            return QgsVectorLayer.AddToSelection
        elif self.mOptionSelectionIntersectSelection.isChecked():
            return QgsVectorLayer.IntersectSelection
        elif self.mOptionRemoveFromSelection.isChecked():
            return QgsVectorLayer.RemoveFromSelection
        else:
            return QgsVectorLayer.SetSelection

    def onSelectBehaviourOptionTriggered(self):

        a: QAction = self.sender()
        m: QMenu = self.mOptionSelectBehaviour.menu()

        if isinstance(a, QAction) and isinstance(m, QMenu) and a in m.actions():
            for ca in m.actions():
                assert isinstance(ca, QAction)
                if ca == a:
                    self.mOptionSelectBehaviour.setIcon(a.icon())
                    self.mOptionSelectBehaviour.setText(a.text())
                    self.mOptionSelectBehaviour.setToolTip(a.toolTip())
                    self.mOptionSelectBehaviour.setChecked(True)
                ca.setChecked(ca == a)

946
    def onLabelFeatureAdded(self, fid):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
947
948
949
        if self.mOptionSelectBehaviour.isChecked():
            lastSelection: typing.List[int] = self.mLayer.selectedFeatureIds()
            self.mLayer.selectByIds([fid], self.selectBehaviour())
950
951
952
953

    def showProperties(self, *args):
        showLayerPropertiesDialog(self.mLayer, None, parent=self, useQGISDialog=True)

954

955
956
class LabelShortcutEditorConfigWidget(QgsEditorConfigWidget):

957
    def __init__(self, vl: QgsVectorLayer, fieldIdx: int, parent: QWidget):
958
959

        super(LabelShortcutEditorConfigWidget, self).__init__(vl, fieldIdx, parent)
960
        loadUi(DIR_UI / 'labelshortcuteditorconfigwidget.ui', self)
961

962
963
        self.cbLabelType: QComboBox
        self.cbLabelGroup: QComboBox
964
965
966
967
968
        assert isinstance(vl, QgsVectorLayer)
        field = vl.fields().at(fieldIdx)
        assert isinstance(field, QgsField)
        self.mAllowedShortCuts = shortcuts(field)
        for i, option in enumerate(self.mAllowedShortCuts):
969
            self.cbLabelType.addItem(option.value, option)
970

971
972
973
974
        self.cbLabelType.currentIndexChanged[int].connect(self.changed.emit)
        self.cbLabelGroup.setEditable(True)
        self.cbLabelGroup.setInsertPolicy(QComboBox.InsertAtTop)
        self.cbLabelGroup.currentIndexChanged.connect(self.changed.emit)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
975
976
        self.btnAddGroup.setDefaultAction(self.actionAddGroup)
        self.actionAddGroup.triggered.connect(self.onAddGroup)
977

Benjamin Jakimow's avatar
Benjamin Jakimow committed
978
        # self.setConfig(vl.editorWidgetSetup(fieldIdx).config())
979
980
        self.mLastConfig = {}

Benjamin Jakimow's avatar
Benjamin Jakimow committed
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
    def onAddGroup(self):

        grp = self.cbLabelGroup.currentText()
        if grp in ['', None]:
            return
        m = self.cbLabelGroup.model()
        if isinstance(m, QStandardItemModel):
            for r in range(m.rowCount()):
                item = m.item(r)
                if grp == item.data(Qt.DisplayRole):
                    return

            #
            newItem = QStandardItem(grp)
            m.appendRow(newItem)

997
998
    def setLayerGroupModel(self, model: QStandardItemModel):
        self.cbLabelGroup.setModel(model)
999

Benjamin Jakimow's avatar
Benjamin Jakimow committed
1000
    def config(self, *args, **kwargs) -> dict:
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
        """
        Return the widget configuration
        :param args:
        :type args:
        :param kwargs:
        :type kwargs:
        :return:
        :rtype:
        """
        conf = createWidgetConf(self.cbLabelType.currentData(),
                                group=self.cbLabelGroup.currentText())

        grp = conf.get(LabelConfigurationKey.LabelGroup, '')
        # add group to group model to share it with other LabelShortcutEditorConfigWidgets
        self.addLabelGroup(grp)
        return conf
1017

1018
    def addLabelGroup(self, grp: str):
1019

1020
        m: QStandardItemModel = self.cbLabelGroup.model()
1021

1022
        if grp not in ['', None] and isinstance(m, QStandardItemModel):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
1023
1024
1025
1026
1027
1028
1029
1030
            exists = False
            for r in range(m.rowCount()):
                item = m.item(r)
                if grp == item.data(Qt.DisplayRole):
                    exists = True
                    break
            if not exists:
                m.appendRow(QStandardItem(grp))
1031

1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
    def setLabelGroup(self, grp):

        if grp is None:
            grp = ''
        else:
            grp = str(grp).strip()

        i = self.cbLabelGroup.findText(grp)
        if i == -1 and grp != '':
            self.addLabelGroup(grp)
            i = self.cbLabelGroup.findText(grp)

        if i >= 0:
            self.cbLabelGroup.setCurrentIndex(i)
            s = ""
        else:
            self.cbLabelGroup.setCurrentText(grp)

    def fieldName(self) -> str:
        field = self.layer().fields().at(self.field())
        return field.name()
1053

1054
    def setConfig(self, config: dict):
1055
        self.mLastConfig = config
1056
1057
        if len(config) > 0:
            s = ""
1058

1059
        labelType: LabelShortcutType = LabelShortcutType.fromConfValue(
Benjamin Jakimow's avatar
Benjamin Jakimow committed
1060
1061
            config.get(LabelConfigurationKey.LabelType, None)
        )
1062
1063
1064

        assert isinstance(labelType, LabelShortcutType)
        labelGroup: str = config.get(LabelConfigurationKey.LabelGroup, '')
1065
1066
1067
        if labelType not in self.mAllowedShortCuts:
            labelType = self.mAllowedShortCuts[0]

1068
1069
        i = self.cbLabelType.findData(labelType)
        self.cbLabelType.setCurrentIndex(i)
1070
1071
1072
1073
        self.setLabelGroup(labelGroup)
        grp2 = self.cbLabelGroup.currentText()
        if grp2 != labelGroup:
            s = ""
1074
1075

    def onIndexChanged(self, *args):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
1076
        self.changed.emit()
1077
1078


1079
1080
class LabelShortcutEditorWidgetWrapper(QgsEditorWidgetWrapper):

1081
    def __init__(self, vl: QgsVectorLayer, fieldIdx: int, editor: QWidget, parent: QWidget):
1082
1083
        super(LabelShortcutEditorWidgetWrapper, self).__init__(vl, fieldIdx, editor, parent)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
1084
    def createWidget(self, parent: QWidget = None) -> QWidget:
1085
1086
1087
        """
        Create the data input widget
        :param parent: QWidget
1088
        :return: QLineEdit | QgsDateTimeEdit | QSpinBox
1089
        """
1090
        # log('createWidget')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
1091
        # labelType = self.configLabelType()
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
        fieldType = self.field().type()
        if fieldType == QVariant.Date:
            return QgsDateEdit(parent)
        elif fieldType == QVariant.DateTime:
            return QgsDateTimeEdit(parent)
        elif fieldType == QVariant.Time:
            return QgsTimeEdit(parent)
        elif fieldType == QVariant.Double:
            return QgsDoubleSpinBox(parent)
        elif fieldType == QVariant.Int:
            return QgsSpinBox(parent)
        else:
            return QLineEdit(parent)
1105

1106
1107
    def initWidget(self, editor: QWidget):
        # log(' initWidget')
1108

1109
        # if isinstance(editor, ClassificationSchemeComboBox):
1110
1111
1112
1113
        #    cs = self.configClassificationScheme()
        #    if isinstance(cs, ClassificationScheme):
        #        self.mEditor.setClassificationScheme(cs)
        #        self.mEditor.currentIndexChanged.connect(self.onValueChanged)
1114

1115
1116
1117
        if isinstance(editor, QLineEdit):
            editor.textChanged.connect(self.onValueChanged)
        elif isinstance(editor, (QgsTimeEdit, QgsDateEdit, QgsDateTimeEdit)):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
1118
1119
1120
1121
1122
1123
            if isinstance(editor, QgsDateEdit):
                editor.setDisplayFormat('yyyy-MM-dd')
            elif isinstance(editor, QgsDateTimeEdit):
                editor.setDisplayFormat('yyyy-MM-dd HH:mm')
            elif isinstance(editor, QgsTimeEdit):
                pass
1124
1125
            editor.clear()
            editor.valueChanged.connect(self.onValueChanged)
1126
        elif isinstance(editor, (QgsDoubleSpinBox, QgsSpinBox)):
1127
            editor.valueChanged.connect(self.onValueChanged)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
1128

1129
1130
        else:
            s = ""
1131
1132
1133
1134
1135

    def onValueChanged(self, *args):
        self.valueChanged.emit(self.value())
        s = ""

Benjamin Jakimow's avatar
Benjamin Jakimow committed
1136
    def valid(self, *args, **kwargs) -> bool:
1137
1138
1139
1140
1141
1142
        """
        Returns True if a valid editor widget exists
        :param args:
        :param kwargs:
        :return: bool
        """
1143
        # return isinstance(self.mEditor, (ClassificationSchemeComboBox, QLineEdit))
1144
1145
        return isinstance(self.widget(), (QLineEdit, QgsDateTimeEdit, QgsTimeEdit,
                                          QgsDateEdit, QgsSpinBox, QgsDoubleSpinBox))
1146
1147

    def value(self, *args, **kwargs):
1148
        """
1149
        Returns the value
1150
1151
1152
1153
        :param args:
        :param kwargs:
        :return:
        """
1154
        typeCode = self.field().type()