Newer
Older
from qgis.core import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *
from osgeo import gdal
from eotimeseriesviewer.externals.qps.layerproperties import *
from eotimeseriesviewer.utils import loadUI, qgisInstance
from eotimeseriesviewer.externals.qps.classification.classificationscheme \
import ClassificationSchemeWidget, ClassificationScheme, ClassInfo, ClassificationSchemeComboBox
from eotimeseriesviewer.timeseries import TimeSeriesDatum

Benjamin Jakimow
committed
#the QgsProject(s) and QgsMapLayerStore(s) to search for QgsVectorLayers
MAP_LAYER_STORES = [QgsProject.instance()]

Benjamin Jakimow
committed
CONFKEY_CLASSIFICATIONSCHEME = 'classificationScheme'

Benjamin Jakimow
committed
CONFKEY_LABELTYPE = 'labelType'

Benjamin Jakimow
committed
class LabelShortcutType(enum.Enum):

Benjamin Jakimow
committed
"""Enumeration for shortcuts to be derived from a TimeSeriesDatum instance"""

Benjamin Jakimow
committed
Off = 'No labeling (default)'
Date = 'Date-Time'
DOY = 'Day of Year (DOY)'
Year = 'Year'
DecimalYear = 'Decimal year'
Sensor = 'Sensor name'

Benjamin Jakimow
committed
Classification = 'Classificaton'
def shortcuts(field:QgsField):
"""
Returns the possible LabelShortCutTypes for a certain field
:param fieldName: str
:return: [list]
"""
assert isinstance(field, QgsField)

Benjamin Jakimow
committed
shortCutsString = [LabelShortcutType.Sensor, LabelShortcutType.Date, LabelShortcutType.Classification]
shortCutsInt = [LabelShortcutType.Year, LabelShortcutType.DOY, LabelShortcutType.Classification]
shortCutsFloat = [LabelShortcutType.Year, LabelShortcutType.DOY, LabelShortcutType.DecimalYear, LabelShortcutType.Classification]

Benjamin Jakimow
committed
options = [LabelShortcutType.Off]
t = field.typeName().lower()
if t == 'string':
options.extend(shortCutsString)
options.extend(shortCutsInt)
options.extend(shortCutsFloat)
elif t.startswith('integer'):
options.extend(shortCutsInt)
elif t.startswith('real'):
options.extend(shortCutsInt)
options.extend(shortCutsFloat)
else:
s = ""

Benjamin Jakimow
committed
result = []
for o in options:
if o not in result:
result.append(o)
return result
def layerClassSchemes(layer:QgsVectorLayer)->list:
assert isinstance(layer, QgsVectorLayer)
schemes = []
for i in range(layer.fields().count()):
setup = layer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
schemes.append(layer)

Benjamin Jakimow
committed

Benjamin Jakimow
committed
def labelShortcutLayerClassificationSchemes(layer:QgsVectorLayer):
"""
Returns the ClassificationSchemes used for labeling shortcuts
:param layer: QgsVectorLayer
:return: [list-of-classificationSchemes]
"""
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()
ci = conf.get(CONFKEY_CLASSIFICATIONSCHEME)
if isinstance(ci, ClassificationScheme) and ci not in classSchemes:
classSchemes.add(ci)
return classSchemes
def labelShortcutLayers()->list:
"""
Returns a list of all known QgsVectorLayer which define at least one LabelShortcutEditWidget
:return: [list-of-QgsVectorLayer]
"""

Benjamin Jakimow
committed
layers = []
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)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:

Benjamin Jakimow
committed
if layer not in layers:
layers.append(layer)

Benjamin Jakimow
committed
return layers
def applyShortcutsToRegisteredLayers(tsd:TimeSeriesDatum, classInfos:list):

Benjamin Jakimow
committed
"""
:param tsd:
:param classInfos:
:return:
"""
for layer in labelShortcutLayers():
assert isinstance(layer, QgsVectorLayer)
applyShortcuts(layer, tsd, classInfos)

Benjamin Jakimow
committed
def applyShortcuts(vectorLayer:QgsVectorLayer, tsd:TimeSeriesDatum, classInfos:dict=None):
"""
Labels selected features with information related to TimeSeriesDatum tsd, according to
the settings specified in this model.
:param tsd: TimeSeriesDatum
:param classInfos:
"""
assert isinstance(tsd, TimeSeriesDatum)

Benjamin Jakimow
committed
assert isinstance(classInfos, (dict, None))
assert isinstance(vectorLayer, QgsVectorLayer)
assert vectorLayer.isEditable()

Benjamin Jakimow
committed
#Lookup-Table for classiicationSchemes
LUT_Classifications = dict()
for i in range(vectorLayer.fields().count()):
field = vectorLayer.fields().at(i)
setup = vectorLayer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
conf = setup.config()
labelType = conf.get(CONFKEY_LABELTYPE)
classScheme = conf.get(CONFKEY_CLASSIFICATIONSCHEME)
if labelType == LabelShortcutType.Classification and isinstance(classScheme, ClassificationScheme):
LUT_Classifications[i] = classScheme
LUT_Classifications[classScheme.name()] = classScheme
LUT_Classifications[field.name()] = classScheme
for i in range(vectorLayer.fields().count()):
setup = vectorLayer.editorWidgetSetup(i)
assert isinstance(setup, QgsEditorWidgetSetup)
if setup.type() == EDITOR_WIDGET_REGISTRY_KEY:
field = vectorLayer.fields().at(i)
assert isinstance(field, QgsField)
conf = setup.config()

Benjamin Jakimow
committed
labelType = conf.get(CONFKEY_LABELTYPE)
if isinstance(labelType, LabelShortcutType):

Benjamin Jakimow
committed
if labelType == LabelShortcutType.Sensor:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.DOY:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.Date:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.DecimalYear:

Benjamin Jakimow
committed
elif labelType == LabelShortcutType.Classification:

Benjamin Jakimow
committed
fieldClassScheme = conf.get(CONFKEY_CLASSIFICATIONSCHEME)
if isinstance(fieldClassScheme, ClassificationScheme):
for ciKey, classInfo in classInfos.items():
classScheme = LUT_Classifications.get(ciKey)
if classScheme == fieldClassScheme and classInfo in fieldClassScheme:
if field.type() == QVariant.String:
value = classInfo.name()
else:
value = classInfo.label()
break
if value == None:
continue
if field.type() == QVariant.String:
value = str(value)
for feature in vectorLayer.selectedFeatures():
assert isinstance(feature, QgsFeature)
oldValue = feature.attribute(field.name())
vectorLayer.changeAttributeValue(feature.id(), i, value, oldValue)
pass
class LabelAttributeTableModel(QAbstractTableModel):
def __init__(self, parent=None, *args):
super(LabelAttributeTableModel, self).__init__()
self.cnField = 'Field'
self.cnFieldType = 'Type'

Benjamin Jakimow
committed
self.cnLabel = 'Label shortcut'
self.mColumnNames = [self.cnField, self.cnFieldType, self.cnLabel]

Benjamin Jakimow
committed
#self.mLabelTypes = dict()
self.mVectorLayer = None
def setVectorLayer(self, layer:QgsVectorLayer):
if isinstance(layer, QgsVectorLayer):
layer.attributeAdded.connect(self.resetModel)
layer.attributeDeleted.connect(self.resetModel)
self.mVectorLayer = layer
else:
self.mVectorLayer = None

Benjamin Jakimow
committed
self.resetModel()

Benjamin Jakimow
committed

Benjamin Jakimow
committed
"""
Returns true if a QgsVectorLayer is specified.
:return: bool
"""
return isinstance(self.mVectorLayer, QgsVectorLayer)
def resetModel(self):
self.beginResetModel()

Benjamin Jakimow
committed
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)

Benjamin Jakimow
committed
#self.mLabelTypes[field.name()] = LabelShortcutType.Off
self.endResetModel()
def rowCount(self, parent = QModelIndex())->int:
if isinstance(self.mVectorLayer, QgsVectorLayer):
return self.mVectorLayer.fields().count()
else:
return 0

Benjamin Jakimow
committed
def fieldName2Index(self, fieldName:str)->str:
assert isinstance(fieldName, str)
if isinstance(self.mVectorLayer, QgsVectorLayer):
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)

Benjamin Jakimow
committed
i = fields.indexOf(fieldName)
return self.createIndex(i, 0)
else:
return QModelIndex()

Benjamin Jakimow
committed
def field2index(self, field:QgsField)->QModelIndex:
assert isinstance(field, QgsField)
return self.fieldName2Index(field.name())

Benjamin Jakimow
committed
def index2editorSetup(self, index:QModelIndex):
if index.isValid() and isinstance(self.mVectorLayer, QgsVectorLayer):
return self.mVectorLayer.editorWidgetSetup(index.row())
else:
return None
def index2field(self, index:QModelIndex)->QgsField:
if index.isValid() and isinstance(self.mVectorLayer, QgsVectorLayer):
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
return fields.at(index.row())
else:
return None
def columnCount(self, parent = QModelIndex())->int:
return len(self.mColumnNames)

Benjamin Jakimow
committed

Benjamin Jakimow
committed
def setFieldShortCut(self, fieldName:str, attributeType:LabelShortcutType):

Benjamin Jakimow
committed
if isinstance(fieldName, QgsField):
fieldName = fieldName.name()
assert isinstance(fieldName, str)

Benjamin Jakimow
committed
assert isinstance(attributeType, LabelShortcutType)

Benjamin Jakimow
committed
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)

Benjamin Jakimow
committed
def shortcuts(self, field:QgsField):
"""
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)
return shortcuts(field)

Benjamin Jakimow
committed
def data(self, index, role = Qt.DisplayRole):
if role is None or not index.isValid():
return None
value = None
columnName = self.mColumnNames[index.column()]

Benjamin Jakimow
committed
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
field = fields.at(index.row())

Benjamin Jakimow
committed
setup = self.mVectorLayer.editorWidgetSetup(index.row())
assert isinstance(setup, QgsEditorWidgetSetup)
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:

Benjamin Jakimow
committed
fac = QgsGui.editorWidgetRegistry().factory(setup.type())
value = fac.name()

Benjamin Jakimow
committed
value = setup
return value
def setData(self, index, value, role=None):
if role is None or not index.isValid():
return None
columnName = self.mColumnNames[index.column()]

Benjamin Jakimow
committed
fields = self.mVectorLayer.fields()
assert isinstance(fields, QgsFields)
field = fields.at(index.row())

Benjamin Jakimow
committed
setup = self.mVectorLayer.editorWidgetSetup(index.row())
assert isinstance(setup, QgsEditorWidgetSetup)
changed = False
if columnName == self.cnLabel and role == Qt.EditRole:

Benjamin Jakimow
committed
if isinstance(value, str):
setup = QgsEditorWidgetSetup(value,{})
self.mVectorLayer.setEditorWidgetSetup(index.row(), setup)
self.dataChanged.emit(index, index, [role])
def columnName(self, index: int)->str:
if isinstance(index, QModelIndex):
if not index.isValid():
return None
index = index.column()
return self.mColumnNames[index]
def flags(self, index):
if index.isValid():
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
if self.columnName(index) == self.cnLabel:
flags = flags | Qt.ItemIsEditable
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
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
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)
def model(self)->LabelAttributeTableModel:
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)
def columnName(self, index:QModelIndex)->str:
if not index.isValid():
return None
return self.model().mColumnNames[index.column()]
def createEditor(self, parent, option, index):
cname = self.columnName(index)

Benjamin Jakimow
committed
model = self.model()
layer = model.mVectorLayer
idx = index.row()
w = None
if index.isValid() and isinstance(model, LabelAttributeTableModel):
if cname == model.cnLabel:
w = QComboBox(parent=parent)

Benjamin Jakimow
committed
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)
return w
def setEditorData(self, editor, index):
cname = self.columnName(index)

Benjamin Jakimow
committed
model = self.model()
layer = model.mVectorLayer
w = None
if index.isValid() and isinstance(model, LabelAttributeTableModel):
if cname == model.cnLabel and isinstance(editor, QComboBox):

Benjamin Jakimow
committed
key = model.data(index, role=Qt.UserRole)
i = -1
for i in range(editor.count()):

Benjamin Jakimow
committed
if editor.itemData(i, role=Qt.UserRole) == key:
editor.setCurrentIndex(i)
break

Benjamin Jakimow
committed
def setModelData(self, w, model, index):

Benjamin Jakimow
committed
assert isinstance(model, LabelAttributeTableModel)
assert isinstance(index, QModelIndex)
cname = model.columnName(index)
if index.isValid() and isinstance(model, LabelAttributeTableModel):
if cname == model.cnLabel and isinstance(w, QComboBox):

Benjamin Jakimow
committed
model.setData(index, w.currentData(Qt.UserRole), Qt.EditRole)
class LabelingDock(QgsDockWidget, loadUI('labelingdock.ui')):

benjamin.jakimow@geo.hu-berlin.de
committed
def __init__(self, parent=None):
super(LabelingDock, self).__init__(parent)

benjamin.jakimow@geo.hu-berlin.de
committed
self.setupUi(self)
assert isinstance(self.mVectorLayerComboBox, QgsMapLayerComboBox)

benjamin.jakimow@geo.hu-berlin.de
committed
#self.mLabelAttributeModel = LabelAttributeTableModel()
self.mVectorLayerComboBox.setAllowEmptyLayer(True)
allowed = ['DB2', 'WFS', 'arcgisfeatureserver', 'delimitedtext', 'memory', 'mssql', 'ogr', 'oracle', 'ows',
'postgres', 'spatialite', 'virtual']
excluded = [k for k in QgsProviderRegistry.instance().providerList() if k not in allowed]
self.mVectorLayerComboBox.setExcludedProviders(excluded)
self.mVectorLayerComboBox.currentIndexChanged.connect(self.onVectorLayerChanged)
self.mDualView = None
self.mCanvas = QgsMapCanvas(self)
self.mCanvas.setVisible(False)
self.initActions()
self.onVectorLayerChanged()

Benjamin Jakimow
committed
def onSelectedRowChanged(self, current:QModelIndex, previous:QModelIndex):
"""
Sets the
:param current:
:param previous:
:return:
"""

Benjamin Jakimow
committed
toRemove = self.scrollAreaWidgetContents.findChildren(QWidget, options=Qt.FindDirectChildrenOnly)

Benjamin Jakimow
committed
for w in toRemove:
self.scrollAreaWidgetContents.layout().removeWidget(w)

Benjamin Jakimow
committed
w.setParent(None)

Benjamin Jakimow
committed
layer = self.mLayerFieldModel.layer()
if current.isValid() and isinstance(layer, QgsVectorLayer):
i = current.row()
field = layer.fields().at(i)
setup = layer.editorWidgetSetup(i)

Benjamin Jakimow
committed
self.labelFieldName.setText('Field "{}"'.format(field.name()))

Benjamin Jakimow
committed
factoryName = setup.type()
if factoryName == '':
factoryName = QgsGui.editorWidgetRegistry().findBest(layer, layer.fields().at(i).name()).type()
self.mCurrentConfigWidget = QgsGui.editorWidgetRegistry().createConfigWidget(factoryName, layer, i, self.scrollAreaWidgetContents)
if isinstance(self.mCurrentConfigWidget, QgsEditorConfigWidget):
self.mLastConf = w.config()
self.mCurrentConfigWidget.changed.connect(self.onCheckApply)
self.scrollAreaWidgetContents.layout().addWidget(self.mCurrentConfigWidget)
self.onCheckApply()

Benjamin Jakimow
committed
def onCheckApply(self):
"""
Checks if any QgsVectorLayer settings have changed and enables/disable buttons
"""
btnApply = self.buttonBoxEditorWidget.button(QDialogButtonBox.Apply)
btnReset = self.buttonBoxEditorWidget.button(QDialogButtonBox.Reset)
changed = False
for w in self.stackedFieldConfigs.findChildren(FieldConfigEditorWidget):
assert isinstance(w, FieldConfigEditorWidget)
if w.changed():
changed = True
break
btnApply.setEnabled(changed)
btnReset.setEnabled(not changed)

Benjamin Jakimow
committed
def onReset(self):
"""
Reloads the QgsVectorLayer and all its current settings
"""
self.onVectorLayerChanged()

Benjamin Jakimow
committed
def onApply(self):
"""
Stores changes settings to current QgsVectorLayer
"""
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
for w in self.stackedFieldConfigs.findChildren(FieldConfigEditorWidget):
assert isinstance(w, FieldConfigEditorWidget)
if w.changed():
config = w.currentFieldConfig()
lyr.setEditFormConfig(config.index(), config.editorWidgetSetup())
self.onVectorLayerChanged()

Benjamin Jakimow
committed
def onVectorLayerChanged(self):
lyr = self.currentVectorSource()
self.layerFieldConfigEditorWidget.setLayer(lyr)
if isinstance(lyr, QgsVectorLayer):
lyr.editingStarted.connect(lambda : self.actionToggleEditing.setChecked(True))
lyr.editingStopped.connect(lambda: self.actionToggleEditing.setChecked(False))
self.mCanvas.setLayers([lyr])
self.mCanvas.setDestinationCrs(lyr.crs())
self.mCanvas.setExtent(lyr.extent())

Benjamin Jakimow
committed
self.mDualView = QgsDualView(self.pageDualView)
self.pageDualView.layout().addWidget(self.mDualView)
self.mDualView.init(lyr, self.mCanvas) # , context=self.mAttributeEditorContext)
self.mDualView.setView(QgsDualView.AttributeTable)
self.mDualView.setAttributeTableConfig(lyr.attributeTableConfig())

Benjamin Jakimow
committed
self.btnBar1.setEnabled(True)
self.btnBar2.setEnabled(True)
else:
self.mCanvas.setLayers([])
if isinstance(self.mDualView, QgsDualView):
self.pageDualView.layout().removeWidget(self.mDualView)

Benjamin Jakimow
committed
self.mDualView = None

Benjamin Jakimow
committed
self.btnBar1.setEnabled(False)
self.btnBar2.setEnabled(False)
def initActions(self):
iface = qgisInstance()
# if isinstance(iface, QgisInterface):
# self.actionAddFeature = iface.actionAddFeature()
# self.actionSaveEdits = iface.actionSaveEdits()
# self.actionCancelEdits = iface.actionCancelEdits()
# self.actionToggleEditing = iface.actionToggleEditing()
# self.actionAddOgrLayer = iface.actionAddOgrLayer()
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
if b:
lyr.startEditing()
else:
lyr.commitChanges()
def onCancelEdits(*args):
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
lyr.rollBack()
def onSaveEdits(*args):
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
b = lyr.isEditable()
lyr.commitChanges()
if b:
lyr.startEditing()
self.actionToggleEditing.toggled.connect(onToggleEditing)
self.actionCancelEdits.triggered.connect(onCancelEdits)
self.actionSaveEdits.triggered.connect(onSaveEdits)

Benjamin Jakimow
committed
self.actionSwitchToTableView.triggered.connect(self.showTableView)
self.actionSwitchToConfigView.triggered.connect(self.showConfigView)
self.actionSwitchToFormView.triggered.connect(self.showFormView)
self.btnAddFeature.setDefaultAction(self.actionAddFeature)
self.btnSaveEdits.setDefaultAction(self.actionSaveEdits)
self.btnCancelEdits.setDefaultAction(self.actionCancelEdits)
self.btnToggleEditing.setDefaultAction(self.actionToggleEditing)
self.btnAddOgrLayer.setDefaultAction(self.actionAddOgrLayer)

Benjamin Jakimow
committed
self.btnAttributeView.setDefaultAction(self.actionSwitchToTableView)
self.btnConfigView.setDefaultAction(self.actionSwitchToConfigView)
self.btnFormView.setDefaultAction(self.actionSwitchToFormView)

Benjamin Jakimow
committed
def showTableView(self):
"""
Call to show the QgsDualView Attribute Table
"""

Benjamin Jakimow
committed
self.stackedWidget.setCurrentWidget(self.pageDualView)
if isinstance(self.mDualView, QgsDualView):
self.mDualView.setView(QgsDualView.AttributeTable)
def showFormView(self):
"""
Call to show the QgsDualView Attribute Editor
"""

Benjamin Jakimow
committed
self.stackedWidget.setCurrentWidget(self.pageDualView)
if isinstance(self.mDualView, QgsDualView):
self.mDualView.setView(QgsDualView.AttributeEditor)
def showConfigView(self):
"""
Call to show the QgsVectorLayer field settings
"""

Benjamin Jakimow
committed
self.stackedWidget.setCurrentWidget(self.pageConfigView)
def currentVectorSource(self)->QgsVectorLayer:
"""
Returns the current QgsVectorLayer
:return: QgsVectorLayer
"""
return self.mVectorLayerComboBox.currentLayer()
class LabelShortcutEditorConfigWidget(QgsEditorConfigWidget):
def __init__(self, vl:QgsVectorLayer, fieldIdx:int, parent:QWidget):
super(LabelShortcutEditorConfigWidget, self).__init__(vl, fieldIdx, parent)
#self.setupUi(self)
self.setLayout(QVBoxLayout())
self.mCBShortCutType = QComboBox(self)
self.mClassWidget = ClassificationSchemeWidget(parent=self)
self.layout().addWidget(self.mCBShortCutType)
self.layout().addWidget(self.mClassWidget)
assert isinstance(vl, QgsVectorLayer)
field = vl.fields().at(fieldIdx)
assert isinstance(field, QgsField)
self.mAllowedShortCuts = shortcuts(field)
for i, option in enumerate(self.mAllowedShortCuts):
self.mCBShortCutType.addItem(option.value, option)
self.mCBShortCutType.currentIndexChanged[int].connect(self.onIndexChanged)
self.mCBShortCutType.currentIndexChanged[int].connect(lambda : self.onIndexChanged())
self.mLastConfig = {}
self.onIndexChanged()
def config(self, *args, **kwargs)->dict:
conf = dict()

Benjamin Jakimow
committed
conf[CONFKEY_LABELTYPE] = self.mCBShortCutType.currentData()
cs = self.mClassWidget.classificationScheme()
assert isinstance(cs, ClassificationScheme)
#todo: json for serialization

Benjamin Jakimow
committed
conf[CONFKEY_CLASSIFICATIONSCHEME] = cs
return conf
def setConfig(self, config:dict):
self.mLastConfig = config

Benjamin Jakimow
committed
labelType = config.get(CONFKEY_LABELTYPE)
if not isinstance(labelType, LabelShortcutType):
labelType = LabelShortcutType.Off
if labelType not in self.mAllowedShortCuts:
labelType = self.mAllowedShortCuts[0]
i = self.mCBShortCutType.findData(labelType)
#self.mCBShortCutType.currentIndexChanged.connect(self.onIndexChanged)
self.mCBShortCutType.setCurrentIndex(i)

Benjamin Jakimow
committed
classScheme = config.get(CONFKEY_CLASSIFICATIONSCHEME)
if isinstance(classScheme, ClassificationScheme):
self.mClassWidget.setClassificationScheme(classScheme)
def onIndexChanged(self, *args):
ltype = self.shortcutType()

Benjamin Jakimow
committed
if ltype == LabelShortcutType.Classification:
self.mClassWidget.setEnabled(True)
else:
self.mClassWidget.setEnabled(False)
def classificationScheme(self)->ClassificationScheme:
return self.mClassWidget.classificationScheme()
def setClassificationScheme(self, classScheme:ClassificationScheme):
assert isinstance(classScheme, ClassificationScheme)
self.mClassWidget.setClassificationScheme(classScheme)

Benjamin Jakimow
committed
def shortcutType(self)->LabelShortcutType:
return self.mCBShortCutType.currentData(Qt.UserRole)
class LabelShortcutEditorWidgetWrapper(QgsEditorWidgetWrapper):
def __init__(self, vl:QgsVectorLayer, fieldIdx:int, editor:QWidget, parent:QWidget):
super(LabelShortcutEditorWidgetWrapper, self).__init__(vl, fieldIdx, editor, parent)
self.mEditor = None
self.mValidator = None

Benjamin Jakimow
committed
def configLabelType(self)->LabelShortcutType:
return self.config(CONFKEY_LABELTYPE)
def configClassificationScheme(self)->ClassificationScheme:

Benjamin Jakimow
committed
return self.config(CONFKEY_CLASSIFICATIONSCHEME)
def createWidget(self, parent: QWidget):
"""
Create the data input widget
:param parent: QWidget
:return: ClassificationSchemeComboBox | default widget
"""
#log('createWidget')
labelType = self.configLabelType()

Benjamin Jakimow
committed
if labelType == LabelShortcutType.Classification:
self.mEditor = ClassificationSchemeComboBox(parent)
else:
self.mEditor = QLineEdit(parent)
self.mValidator = QRegExpValidator()
return self.mEditor
def initWidget(self, editor:QWidget):
#log(' initWidget')
if isinstance(editor, ClassificationSchemeComboBox):
cs = self.configClassificationScheme()
if isinstance(cs, ClassificationScheme):
self.mEditor.setClassificationScheme(cs)
self.mEditor.currentIndexChanged.connect(self.onValueChanged)
if isinstance(editor, QLineEdit):
self.mEditor = editor
self.mEditor.textChanged.connect(self.onValueChanged)
def onValueChanged(self, *args):
self.valueChanged.emit(self.value())
s = ""
def valid(self, *args, **kwargs)->bool:
"""
Returns True if a valid editor widget exists
:param args:
:param kwargs:
:return: bool
"""
return isinstance(self.mEditor, (ClassificationSchemeComboBox, QLineEdit))
def value(self, *args, **kwargs):
"""
Reuturns the value
:param args:
:param kwargs:
:return:
"""
typeCode = self.field().type()
if isinstance(self.mEditor, ClassificationSchemeComboBox):
classInfo = self.mEditor.currentClassInfo()
if isinstance(classInfo, ClassInfo):
if typeCode == QVariant.String:
return classInfo.name()
if typeCode in [QVariant.Int, QVariant.Double]:
return classInfo.label()
elif isinstance(self.mEditor, QLineEdit):
txt = self.mEditor.text()
if len(txt) == '':
return self.defaultValue()
if typeCode == QVariant.String:
return txt
try:
if typeCode == QVariant.Int:
return int(txt)
if typeCode == QVariant.Double:
return float(txt)
return self.mLineEdit.text()
return self.defaultValue()
def setEnabled(self, enabled:bool):
if isinstance(self.mEditor, QWidget):
self.mEditor.setEnabled(enabled)
def setValue(self, value):
if isinstance(self.mEditor, ClassificationSchemeComboBox):
cs = self.mEditor.classificationScheme()
if isinstance(cs, ClassificationScheme) and len(cs) > 0:
i = cs.classIndexFromValue(value)
self.mEditor.setCurrentIndex(max(i, 0))
elif isinstance(self.mEditor, QLineEdit):
if value in [QVariant(), None]:
self.mEditor.setText(None)
else:
self.mEditor.setText(str(value))
class LabelShortcutWidgetFactory(QgsEditorWidgetFactory):
def __init__(self, name:str):
super(LabelShortcutWidgetFactory, self).__init__(name)
self.mConfigurations = {}

Benjamin Jakimow
committed
def name(self)->str:
return 'EOTSV Label Shortcut'
def configWidget(self, layer:QgsVectorLayer, fieldIdx:int, parent=QWidget)->LabelShortcutEditorConfigWidget:
"""
Returns a SpectralProfileEditorConfigWidget
:param layer: QgsVectorLayer
:param fieldIdx: int
:param parent: QWidget
:return: SpectralProfileEditorConfigWidget
"""
w = LabelShortcutEditorConfigWidget(layer, fieldIdx, parent)
key = self.configKey(layer, fieldIdx)
w.setConfig(self.readConfig(key))
w.changed.connect(lambda : self.writeConfig(key, w.config()))
return w
def configKey(self, layer:QgsVectorLayer, fieldIdx:int):
"""
Returns a tuple to be used as dictionary key to identify a layer field configuration.
:param layer: QgsVectorLayer
:param fieldIdx: int
:return: (str, int)
"""
return (layer.id(), fieldIdx)
def create(self, layer:QgsVectorLayer, fieldIdx:int, editor:QWidget, parent:QWidget)->LabelShortcutEditorWidgetWrapper:
"""
Create a ClassificationSchemeEditorWidgetWrapper
:param layer: QgsVectorLayer
:param fieldIdx: int
:param editor: QWidget
:param parent: QWidget
:return: ClassificationSchemeEditorWidgetWrapper
"""
w = LabelShortcutEditorWidgetWrapper(layer, fieldIdx, editor, parent)
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
return w
def writeConfig(self, key:tuple, config:dict):
"""
:param key: tuple (str, int), as created with .configKey(layer, fieldIdx)
:param config: dict with config values
"""
self.mConfigurations[key] = config
def readConfig(self, key:tuple):
"""
:param key: tuple (str, int), as created with .configKey(layer, fieldIdx)
:return: {}
"""
if key in self.mConfigurations.keys():
conf = self.mConfigurations[key]
else:
#return the very default configuration
conf = {}
return conf
def fieldScore(self, vl:QgsVectorLayer, fieldIdx:int)->int:
"""
This method allows disabling this editor widget type for a certain field.
0: not supported: none String fields
5: maybe support String fields with length <= 400
20: specialized support: String fields with length > 400
:param vl: QgsVectorLayer
:param fieldIdx: int
:return: int
"""
#log(' fieldScore()')
if fieldIdx < 0:
return 0
field = vl.fields().at(fieldIdx)
assert isinstance(field, QgsField)

Benjamin Jakimow
committed
if field.type() in [QVariant.String, QVariant.Int]:
return 20
else:
return 0 #no support
def supportsField(self, vl:QgsVectorLayer, idx:int):