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

Benjamin Jakimow
committed
from eotimeseriesviewer.timeseries import TimeSeriesDate, TimeSeriesSource

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):
"""Enumeration for shortcuts to be derived from a TimeSeriesDate instance"""
Off = 'No Quick Label (default)'
Date = 'Date-Time'
DOY = 'Day of Year (DOY)'
Year = 'Year'
DecimalYear = 'Decimal Year'
Sensor = 'Sensor Name'

Benjamin Jakimow
committed
SourceImage = 'Source Image'
#Classification = 'Classification'
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.SourceImage]
shortCutsInt = [LabelShortcutType.Year, LabelShortcutType.DOY]
shortCutsFloat = [LabelShortcutType.Year, LabelShortcutType.DOY, LabelShortcutType.DecimalYear]

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:
"""
Returns a list of (ClassificationScheme, QgsField) for all QgsFields with QgsEditorWidget being QgsClassificationWidgetWrapper or RasterClassification.
:param layer: QgsVectorLayer
:return: list [(ClassificationScheme, QgsField), ...]
"""
assert isinstance(layer, QgsVectorLayer)
from .externals.qps.classification.classificationscheme import EDITOR_WIDGET_REGISTRY_KEY as CS_KEY
from .externals.qps.classification.classificationscheme import classSchemeFromConfig
schemes = []
for i in range(layer.fields().count()):
setup = layer.editorWidgetSetup(i)
field = layer.fields().at(i)
assert isinstance(field, QgsField)
assert isinstance(setup, QgsEditorWidgetSetup)
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))

Benjamin Jakimow
committed

Benjamin Jakimow
committed
def labelShortcutLayerClassificationSchemes(layer:QgsVectorLayer):
"""
Returns the ClassificationSchemes + QgsField used for labeling shortcuts

Benjamin Jakimow
committed
:param layer: QgsVectorLayer
:return: [(ClassificationScheme, QgsField), (ClassificationScheme, QgsField), ...]

Benjamin Jakimow
committed
"""
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.append((ci, layer.fields().at(i)))

Benjamin Jakimow
committed
return classSchemes
Returns a list of known QgsVectorLayers with at least one LabelShortcutEditWidget
:return: [list-of-QgsVectorLayer]
"""

Benjamin Jakimow
committed
layers = []
from .externals.qps.classification.classificationscheme import EDITOR_WIDGET_REGISTRY_KEY as CS_KEY
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() in [EDITOR_WIDGET_REGISTRY_KEY, CS_KEY, 'Classification']:

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

Benjamin Jakimow
committed
return layers

Benjamin Jakimow
committed
def setQuickTSDLabelsForRegisteredLayers(tsd:TimeSeriesDate, tss:TimeSeriesSource):

Benjamin Jakimow
committed
"""

Benjamin Jakimow
committed
:param classInfos:
"""
assert isinstance(layer, QgsVectorLayer)

Benjamin Jakimow
committed
setQuickTSDLabels(layer, tsd, tss)
def setQuickClassInfo(vectorLayer:QgsVectorLayer, field, classInfo:ClassInfo):
"""
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)
assert isinstance(classInfo, ClassInfo)
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()

Benjamin Jakimow
committed
def setQuickTSDLabels(vectorLayer:QgsVectorLayer, tsd:TimeSeriesDate, tss:TimeSeriesSource):
Labels selected features with information related to TimeSeriesDate tsd, according to

Benjamin Jakimow
committed
the settings specified in this model. Note: this will not the any ClassInfo or the source image values
assert isinstance(tsd, TimeSeriesDate)
assert isinstance(vectorLayer, QgsVectorLayer)
vectorLayer.beginEditCommand('Quick labels {}'.format(tsd.date()))

Benjamin Jakimow
committed
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):
if labelType == LabelShortcutType.Off:
pass

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.SourceImage and isinstance(tss, TimeSeriesSource):
value = tss.uri()
#elif labelType == LabelShortcutType.Classification:
# pass
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)

Benjamin Jakimow
committed
vectorLayer.endEditCommand()
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
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
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)

Benjamin Jakimow
committed
class LabelingWidget(QMainWindow, loadUI('labelingdock.ui')):
sigVectorLayerChanged = pyqtSignal()
sigMapExtentRequested = pyqtSignal(SpatialExtent)
sigMapCenterRequested = pyqtSignal(SpatialPoint)
def __init__(self, parent=None, canvas=None):

Benjamin Jakimow
committed
super(LabelingWidget, self).__init__(parent)

benjamin.jakimow@geo.hu-berlin.de
committed
self.setupUi(self)

Benjamin Jakimow
committed
self.mVectorLayerComboBox = QgsMapLayerComboBox()
self.mVectorLayerComboBox.setAllowEmptyLayer(True)

Benjamin Jakimow
committed
self.mVectorLayerComboBox.setShowCrs(True)
self.mVectorLayerComboBox.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
self.mVectorLayerComboBox.setMinimumWidth(150)

Benjamin Jakimow
committed
assert isinstance(self.mVectorLayerComboBox, QgsMapLayerComboBox)
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)

Benjamin Jakimow
committed
self.toolBarSelectVectorSource.addWidget(self.mVectorLayerComboBox)
if not isinstance(canvas, QgsMapCanvas):
canvas = QgsMapCanvas(self)
canvas.setVisible(False)
self.mCanvas = canvas
self.initActions()
self.onVectorLayerChanged()
def canvas(self)->QgsMapCanvas:
Returns the internal map canvas
:return: QgsMapCanvas
return self.mCanvas

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 isModified(self)->bool:
return isinstance(self.currentVectorSource(), QgsVectorLayer) and self.currentVectorSource().isModified()
def onVectorLayerChanged(self):
lyr = self.currentVectorSource()
# remove old QgsDualView
if isinstance(self.mDualView, QgsDualView):
self.mDualView.setParent(None)
self.mDualView = None
i = self.stackedWidget.count() - 1
while i >= 0:
self.stackedWidget.widget(i).setParent(None)
i -= 1
self.mCanvas.setLayers([])
assert isinstance(self.stackedWidget, QStackedWidget)

Benjamin Jakimow
committed
if isinstance(lyr, QgsVectorLayer):
lyr.editingStarted.connect(lambda: self.mActionToggleEditing.setChecked(True))
lyr.editingStopped.connect(lambda: self.mActionToggleEditing.setChecked(False))
lyr.allowCommitChanged.connect(self.updateActions)
self.mCanvas.setLayers([lyr])

Benjamin Jakimow
committed
if not self.mCanvas.mapSettings().destinationCrs().isValid():
self.mCanvas.setDestinationCrs(lyr.crs())
if self.mCanvas.extent().width() == 0:
self.mCanvas.setExtent(lyr.extent())
self.mDualView = QgsDualView(self)
# change selected row color: keep color also when attribute table looses focus
pal = self.mDualView.tableView().palette()
cSelected = pal.color(QPalette.Active, QPalette.Highlight)
pal.setColor(QPalette.Inactive, QPalette.Highlight, cSelected)
self.mDualView.tableView().setPalette(pal)
self.stackedWidget.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.updateActions()
self.sigVectorLayerChanged.emit()
def updateActions(self, *args):
if isinstance(self.currentVectorSource(), QgsVectorLayer):
b = self.currentVectorSource().isEditable()

Benjamin Jakimow
committed

Benjamin Jakimow
committed
self.mActionToggleEditing.setChecked(b)
self.mActionAddFeature.setEnabled(b)

Benjamin Jakimow
committed
gType = self.currentVectorSource().geometryType()
if gType == QgsWkbTypes.PointGeometry:
self.mActionAddFeature.setIcon(QIcon(':/images/themes/default/mActionCapturePoint.svg'))
elif gType == QgsWkbTypes.LineGeometry:
self.mActionAddFeature.setIcon(QIcon(':/images/themes/default/mActionCaptureLine.svg'))
elif gType == QgsWkbTypes.PolygonGeometry:
self.mActionAddFeature.setIcon(QIcon(':/images/themes/default/mActionCapturePolygon.svg'))
else:
# unknown geometry type?
self.mActionAddFeature.setIcon(QIcon(':/images/themes/default/mActionCapturePolygon.svg'))
buffer = self.currentVectorSource().editBuffer()
b2 = b and isinstance(buffer, QgsVectorLayerEditBuffer)
self.mActionCancelEdits.setEnabled(b2)
self.mActionSaveEdits.setEnabled(b2)

Benjamin Jakimow
committed
self.mActionToggleEditing.setEnabled(True)
for action in self.toolBarSelectFeatures.actions():
action.setEnabled(True)

Benjamin Jakimow
committed
else:

Benjamin Jakimow
committed
for action in self.toolBarSelectFeatures.actions():
action.setEnabled(False)

Benjamin Jakimow
committed
for action in self.toolBarModifyFeatures.actions():
action.setEnabled(False)
def actionAddFeature(self)->QAction:
return self.mActionAddFeature
def actionSaveEdits(self)->QAction:
return self.mActionSaveEdits
def actionToggleEditing(self)->QAction:
return self.mActionToggleEditing
def onToggleEditing(self, b: bool):
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
if b:
lyr.startEditing()
else:
if lyr.isModified():
r = QMessageBox.question(self, '{}'.format(lyr.name()), 'Save changes?')
if r == QMessageBox.Yes:
lyr.commitChanges()
else:
lyr.rollBack()
else:
lyr.commitChanges()
self.updateActions()
def cancelEdits(self, *args):
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
b = lyr.isEditable()
lyr.rollBack()
if b:
lyr.startEditing()
def saveEdits(self, *args):
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
b = lyr.isEditable()
lyr.commitChanges()
if b:
lyr.startEditing()
def initActions(self):
iface = qgisInstance()
# if isinstance(iface, QgisInterface):
# self.mActionAddFeature = iface.actionAddFeature()
# self.mActionSaveEdits = iface.actionSaveEdits()
# self.mActionCancelEdits = iface.actionCancelEdits()
# self.mActionToggleEditing = iface.actionToggleEditing()
# self.mActionAddOgrLayer = iface.actionAddOgrLayer()
self.mActionToggleEditing.toggled.connect(self.onToggleEditing)
self.mActionCancelEdits.triggered.connect(self.cancelEdits)
self.mActionSaveEdits.triggered.connect(self.saveEdits)
self.mActionSwitchToTableView.triggered.connect(self.showTableView)
self.mActionShowLayerProperties.triggered.connect(self.showLayerProperties)
self.mActionSwitchToFormView.triggered.connect(self.showFormView)
self.mActionSelectAll.triggered.connect(self.selectAll)
self.mActionInvertSelection.triggered.connect(self.invertSelection)
self.mActionRemoveSelection.triggered.connect(self.removeSelection)
self.mActionPanMapToSelectedRows.triggered.connect(self.panMapToSelectedRows)
self.mActionZoomMapToSelectedRows.triggered.connect(self.zoomMapToSelectedRows)

Benjamin Jakimow
committed
self.mActionNextFeature.triggered.connect(self.nextFeature)
self.mActionPreviousFeature.triggered.connect(self.previousFeature)
self.btnAttributeView.setDefaultAction(self.mActionSwitchToTableView)
self.btnShowLayerProperties.setDefaultAction(self.mActionShowLayerProperties)
self.btnFormView.setDefaultAction(self.mActionSwitchToFormView)

Benjamin Jakimow
committed
def nextFeature(self):
"""
Selects the next feature and moves the map extent to.
"""
vl = self.currentVectorSource()
if isinstance(vl, QgsVectorLayer) and vl.hasFeatures():
allIDs = sorted(vl.allFeatureIds())

Benjamin Jakimow
committed
fids = vl.selectedFeatureIds()
if len(fids) == 0:

Benjamin Jakimow
committed
else:
i = min(allIDs.index(fids[0]) + 1, len(allIDs)-1)
nextFID = allIDs[i]

Benjamin Jakimow
committed
vl.selectByIds([nextFID])
self.panMapToSelectedRows()
def previousFeature(self):
"""
Selects the previous feature and moves the map extent to.
"""
vl = self.currentVectorSource()
if isinstance(vl, QgsVectorLayer) and vl.hasFeatures():
allIDs = sorted(vl.allFeatureIds())

Benjamin Jakimow
committed
fids = vl.selectedFeatureIds()
if len(fids) == 0:

Benjamin Jakimow
committed
else:
i = max(allIDs.index(fids[0]) - 1, 0)
nextFID = allIDs[i]

Benjamin Jakimow
committed
vl.selectByIds([nextFID])
self.panMapToSelectedRows()
def selectAll(self):
if isinstance(self.currentVectorSource(), QgsVectorLayer):
self.currentVectorSource().selectAll()
def invertSelection(self):
if isinstance(self.currentVectorSource(), QgsVectorLayer):
self.currentVectorSource().invertSelection()
def removeSelection(self):
if isinstance(self.currentVectorSource(), QgsVectorLayer):
self.currentVectorSource().removeSelection()
def panMapToSelectedRows(self):
"""
Pan to the selected layer features
Requires that external maps respond to sigMapCenterRequested
"""
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
crs = self.canvas().mapSettings().destinationCrs()

Benjamin Jakimow
committed
bbox = lyr.boundingBoxOfSelected()
center = SpatialPoint(lyr.crs(), bbox.center()).toCrs(crs)
self.mCanvas.setCenter(center)
self.sigMapCenterRequested.emit(center)
def zoomMapToSelectedRows(self):
"""
Zooms to the selected rows.
Requires that external maps respond to sigMapExtentRequested
"""
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
crs = self.canvas().mapSettings().destinationCrs()
bbox = SpatialExtent(lyr.crs(), lyr.boundingBoxOfSelected()).toCrs(crs)

Benjamin Jakimow
committed
if bbox.width() > 0 and bbox.height() > 0:
self.mCanvas.setExtent(bbox)
self.sigMapExtentRequested.emit(bbox)

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

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

Benjamin Jakimow
committed
if isinstance(self.mDualView, QgsDualView):
self.mDualView.setView(QgsDualView.AttributeEditor)
def showLayerProperties(self):
"""
Call to show the QgsVectorLayer field settings
"""
from .externals.qps.layerproperties import showLayerPropertiesDialog
lyr = self.currentVectorSource()
if isinstance(lyr, QgsVectorLayer):
showLayerPropertiesDialog(lyr, self.mCanvas, parent=self, useQGISDialog=True)
def setCurrentVectorSource(self, layer:QgsVectorLayer):
"""
Sets the current vector source.
:param layer: QgsVectorLayer
"""
assert isinstance(layer, QgsVectorLayer)
if layer not in QgsProject.instance().mapLayers().values():
QgsProject.instance().addMapLayer(layer)

Benjamin Jakimow
committed
cboxLayers = [self.mVectorLayerComboBox.layer(i) for i in range(self.mVectorLayerComboBox.count())]
if layer in cboxLayers:
self.mVectorLayerComboBox.setCurrentIndex(cboxLayers.index(layer))
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
#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)
#classScheme = config.get(CONFKEY_CLASSIFICATIONSCHEME)
#if isinstance(classScheme, ClassificationScheme):
# self.mClassWidget.setClassificationScheme(classScheme)
def onIndexChanged(self, *args):
ltype = self.shortcutType()
#if ltype == LabelShortcutType.Classification:
# self.mClassWidget.setEnabled(True)
# self.mClassWidget.setVisible(True)
#else:
# self.mClassWidget.setEnabled(False)
# self.mClassWidget.setVisible(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:
# 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()
#if labelType == LabelShortcutType.Classification:
# self.mEditor = ClassificationSchemeComboBox(parent)
#else:
self.mEditor = QLineEdit(parent)
self.mValidator = QRegExpValidator()