diff --git a/.gitignore b/.gitignore index 3e786b1..428d99e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ tools/*.py images/ Ui*.py xplanung.zip +schema/* +__pycache__/* +.settings +.pydevproject +.project +.gitignore \ No newline at end of file diff --git a/Ui_Hoehenmanager.ui b/Ui_Hoehenmanager.ui new file mode 100644 index 0000000..be0f057 --- /dev/null +++ b/Ui_Hoehenmanager.ui @@ -0,0 +1,135 @@ + + + Hoehenmananger + + + + 0 + 0 + 397 + 462 + + + + Hoehenmanager + + + true + + + true + + + + + + + + Filterbegriff eingeben + + + + + + + Nur dem Filter entsprechende Einträge anzeigen + + + Filtern + + + + + + + + + Bitte wählen Sie ein Flächenobjekt aus, um die dazugehörige Höhe zu bearbeiten bzw. ein neues Höhenobjekt anzulegen. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + 0 + 250 + + + + + + + + Qt::CustomContextMenu + + + Rechtsklick zum Bearbeiten + + + QAbstractItemView::NoEditTriggers + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Reset + + + + + + + + QgsMapCanvas + QWidget +
qgis.gui
+ 1 +
+
+ + + + buttonBox + accepted() + Hoehenmananger + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Hoehenmananger + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/XPImport.py b/XPImport.py index 0c0aaff..a80071a 100644 --- a/XPImport.py +++ b/XPImport.py @@ -68,7 +68,7 @@ def importGml(self): commands = ['ogr2ogr'] + arguments fused_command = ' '.join([str(c) for c in commands]) - + self.tools.showWarning(fused_command) try: proc = subprocess.check_output( fused_command, diff --git a/XPTools.py b/XPTools.py index bdaed8c..ad007b3 100644 --- a/XPTools.py +++ b/XPTools.py @@ -31,6 +31,10 @@ from .XPlanDialog import BereichsauswahlDialog from .XPlanDialog import StilauswahlDialog +from traceback import format_stack +import inspect +from pprint import pformat + class XPTools(): def __init__(self, iface, standardName, simpleStyleName): self.iface = iface @@ -267,8 +271,8 @@ def getBereicheFuerFeatures(self, db, gids): def getMaxGid(self, db, schemaName, tableName, pkFieldName = "gid"): retValue = -9999 - sel = "SELECT " + pkFieldName + " FROM \"" + schemaName + "\".\"" + tableName + \ - "\" ORDER BY 1 DESC LIMIT 1;" + sel = 'SELECT "' + pkFieldName + '" FROM \"' + schemaName + '"."' + tableName + \ + '" ORDER BY 1 DESC LIMIT 1;' query = QtSql.QSqlQuery(db) query.prepare(sel) @@ -565,14 +569,26 @@ def createFeature(self, layer, fid = None): newFeature = QgsVectorLayerUtils.createFeature(layer) if fid: - newFeaturesetId(fid) + newFeaturesetId(fid) #is this defined? provider = layer.dataProvider() fields = layer.fields() newFeature.initAttributes(fields.count()) for i in range(fields.count()): - newFeature.setAttribute(i,provider.defaultValue(i)) + ## bad fix for bug in Default retrieval from postgres + ## (defaultValue won't give 9999 integer for e.g. XP_ExterneReferenz default value, + ## but defaultValueClause does. Though defaultValueClause will return '' + ## where None/NULL is correct) + ## REALLY DON'T KNOW IF THERE IS SOMEWHERE A CORRECT DEFAULT EMPTY STRING, WHICH + ## SHOULDN'T BE FIXED... + if provider.defaultValueClause(i)=='': + newFeature.setAttribute(i,provider.defaultValue(i)) + else: + newFeature.setAttribute(i,provider.defaultValueClause(i)) + ## end fix + ## old: + ## newFeature.setAttribute(i,provider.defaultValue(i)) return newFeature else: @@ -619,6 +635,27 @@ def setEditable(self, layer, showErrorMsg = False, iface = None): def noActiveLayerWarning(self): self.showWarning(u"Kein aktiver Layer") + def debug(self, msg, stacksize=0): + t_ = [ + x[0].f_locals for x in inspect.trace() + ] + if stacksize==0: + self.log("Debug" + "\n" + msg) + elif len(t_) >=stacksize: + t_ = ['...'] + t_[-1*stacksize:] + self.log("Debug" + "\n" + msg + ' | ' + pformat( t_ )) + else: + self.log("Debug" + "\n" + msg + ' | ' + pformat( t_ )) + + def log(self, msg, type = 'info'): + if type == 'info': + msgType = Qgis.Info + if type == 'warn': + msgType = Qgis.Warning + if type == 'error': + msgType = Qgis.Critical + QgsMessageLog.logMessage(msg, "XPlanung", msgType) + def showQueryError(self, query): self.showError( "%(error)s" % {"error": query.lastError().text()}, "DB-Fehler") @@ -638,24 +675,13 @@ def showWarning(self, msg, title = "XPlanung"): def showError(self, msg, title = "XPlanung"): self.iface.messageBar().pushMessage(title, - msg, level=Qgis.Critical, duration = 10) - self.log(msg, "error") + msg + " (more in log)", level=Qgis.Critical, duration = 10) + self.log(msg +'\n' + str(format_stack()), "error") + self.debug(msg, 2) def noStyleWarning(self, layer): self.showWarning(u"Für den Layer " + layer.name() + u" sind keine Stile gespeichert") - def debug(self, msg): - self.log("Debug" + "\n" + msg) - - def log(self, msg, type = 'info'): - if type == 'info': - msgType = Qgis.Info - if type == 'warn': - msgType = Qgis.Warning - if type == 'error': - msgType = Qgis.Critical - QgsMessageLog.logMessage(msg, "XPlanung", msgType) - def getAuthUserNamePassword(self, authcfg): username = None password = None diff --git a/XPlan.py b/XPlan.py index b374283..227389b 100644 --- a/XPlan.py +++ b/XPlan.py @@ -30,6 +30,7 @@ try: from DataDrivenInputMask.ddattribute import DdTable + from DataDrivenInputMask.dderror import FatalError except: pass @@ -42,7 +43,7 @@ from .XPImport import XPImporter from .XPlanDialog import XPlanungConf from .XPlanDialog import ChooseObjektart -from .XPlanDialog import XPNutzungsschablone, BereichsmanagerDialog, ReferenzmanagerDialog, ImportDialog +from .XPlanDialog import XPNutzungsschablone, BereichsmanagerDialog, ReferenzmanagerDialog, HoehenmanagerDialog, ImportDialog class XpError(object): '''General error''' @@ -193,6 +194,8 @@ def initGui(self): self.action24.triggered.connect(self.loadSO) self.action25 = QtWidgets.QAction(u"ExterneReferenzen bearbeiten", self.iface.mainWindow()) self.action25.triggered.connect(self.referenzmanagerStarten) + self.action25b = QtWidgets.QAction(u"Hoehenangaben bearbeiten", self.iface.mainWindow()) + self.action25b.triggered.connect(self.hoehenmanagerStarten) self.action26 = QtWidgets.QAction(u"räuml. Geltungsbereiche neu berechnen", self.iface.mainWindow()) self.action26.triggered.connect(self.geltungsbereichBerechnen) @@ -205,7 +208,7 @@ def initGui(self): self.action30 = QtWidgets.QAction(u"Importieren", self.iface.mainWindow()) self.action30.triggered.connect(self.importData) - self.xpMenu.addActions([self.action20, self.action25, self.action29, + self.xpMenu.addActions([self.action20, self.action25, self.action25b, self.action29, self.action6, self.action10, self.action30]) self.bereichMenu.addActions([self.action3, self.action1, self.action4]) self.bpMenu.addActions([self.action21, self.action26, self.action28]) @@ -249,8 +252,8 @@ def unload(self): except: pass - def debug(self, msg): - self.tools.log("Debug" + "\n" + msg) + def debug(self, msg, stacksize=0): + self.tools.debug(msg, stacksize=stacksize) def loadLayerLayer(self): self.layerLayer = self.getLayerForTable("QGIS", "layer") @@ -706,6 +709,22 @@ def referenzmanagerStarten(self): dlg.show() dlg.exec_() + def hoehenmanagerStarten(self): + if self.db == None: + self.initialize(False) + + if self.db != None: + refSchema = "XP_Sonstiges" + refTable = "XP_Hoehenangabe" + extRefLayer = self.getLayerForTable(refSchema, refTable) + # self.tools.debug(str(type(extRefLayer))) + + if extRefLayer != None: + self.app.xpManager.moveLayerToGroup(extRefLayer, refSchema) + dlg = HoehenmanagerDialog(self, extRefLayer) + dlg.show() + dlg.exec_() + def onLayerDestroyed(self, layer): '''Slot, der aufgerufen wird wenn ein XP-Layer aus dem Projekt entfernt wird erst in QGIS3 wird das Layerobjekt übergeben''' @@ -890,17 +909,25 @@ def loadTable(self, schemaName, tableName, geomColumn, ddTable = self.app.xpManager.createDdTable(self.db, schemaName, tableName, withOid = False, withComment = False) - - isView = ddTable == None + + isView = ddTable is None #Why does this not hit on "BP_Basisobjekte.BP_Flaechenobjekte"? if isView: ddTable = DdTable(schemaName = schemaName, tableName = tableName) if self.app.xpManager.existsInDb(ddTable, self.db): - thisLayer = self.app.xpManager.loadPostGISLayer(self.db, - ddTable, displayName = displayName, - geomColumn = geomColumn, keyColumn = "gid", - whereClause = filter, intoDdGroup = False) + try: + thisLayer = self.app.xpManager.loadPostGISLayer( + self.db, + ddTable, + displayName = displayName, + geomColumn = geomColumn, + keyColumn = "gid", + whereClause = filter, + intoDdGroup = False + ) + except FatalError as fa: + raise fa return [thisLayer, isView] @@ -1109,30 +1136,79 @@ def layerStyleSlot(self): if stil != None: self.tools.useStyle(layer, stil) - def getLayerForTable(self, schemaName, tableName, - geomColumn = None, showMsg = True): + def findPostgresLayer(self, db, ddTable=None, schemaName=None, tableName=None): + """partly borrowed from ddmanager""" + if not (ddTable is None): + return self.app.xpManager.findPostgresLayer(self.db, ddTable) + elif not (schemaName is None or tableName is None): + for aTreeLayer in QgsProject.instance().layerTreeRoot().findLayers(): + layer = aTreeLayer.layer() + if not (layer is None): + if 0 == layer.type(): # vectorLayer + src = layer.source() + if ( + ("table=\"" + schemaName + "\".\"" + tableName + "\"" in src) and + (db.databaseName() in src) and + (db.hostName() in src) + ): + return layer + return None + else: + return None + + def getQgisLayerForTable(self, schemaName, tableName, **kwargs): + """workaround name for method""" + kwargs['ignoreDD']=True + return self.getLayerForTable(schemaName, tableName, **kwargs) + + def getDDCoveredLayerForTable(self, schemaName, tableName, **kwargs): + """more name for method""" + kwargs['ignoreDD']=False + return self.getLayerForTable(schemaName, tableName, **kwargs) + + def getLayerForTable(self, schemaName, tableName, **kwargs): '''Den Layer schemaName.tableName finden bzw. laden. Wenn geomColumn == None wird geoemtrielos geladen''' - + + #manage defaults/kwargs + geomColumn = kwargs.get('geomColumn', None) + showMsg = kwargs.get('showMsg', True) + filterOnNew = kwargs.get('filterOnNew', "") + displayName = kwargs.get('displayName', None) + ignoreDD = kwargs.get('ignoreDD', False) + #end kwargs + + if displayName is None: + displayName = tableName ddTable = self.app.xpManager.createDdTable( self.db, schemaName, tableName, withOid = False, withComment = False) - if ddTable != None: - layer = self.app.xpManager.findPostgresLayer( - self.db, ddTable) - - if layer == None: - layer = self.loadTable(schemaName, tableName, - geomColumn = geomColumn)[0] - - if layer == None: + if (not (ddTable is None)) or ignoreDD: + layer = self.findPostgresLayer( #not using app.xpManager/ddmanager directly + self.db, + ddTable, #if not None, following parameters are ignored + schemaName=schemaName, tableName=tableName) + + if layer is None: + layer = self.loadTable( + schemaName, + tableName, + geomColumn = geomColumn, + filter=filterOnNew, + displayName=displayName + )[0] + + if layer is None: if showMsg: XpError(u"Kann Tabelle %(schema)s.%(table)s nicht laden!" % \ {"schema":schemaName, "table":tableName}, self.iface) return None else: + if (ddTable is None): #for views + self.app.xpManager.createGroup(schemaName, False) #doesn't add group if already exists. + self.app.xpManager.moveLayerToGroup(layer, schemaName) return layer else: return layer diff --git a/XPlanDialog.py b/XPlanDialog.py index 16b475a..3255c0e 100644 --- a/XPlanDialog.py +++ b/XPlanDialog.py @@ -21,6 +21,9 @@ """ # Import the PyQt and QGIS libraries from qgis.PyQt import QtCore, QtWidgets, QtSql, uic +from qgis.PyQt.QtWidgets import QAction +from qgis.PyQt.QtGui import QFont +from qgis.core import QgsFillSymbol from builtins import str from builtins import range from qgis.core import * @@ -55,6 +58,9 @@ REFERENZMANAGER_CLASS, _ = uic.loadUiType(os.path.join( os.path.dirname(__file__), 'Ui_Referenzmanager.ui')) +HOEHENMANAGER_CLASS, _ = uic.loadUiType(os.path.join( + os.path.dirname(__file__), 'Ui_Hoehenmanager.ui')) + IMPORT_CLASS, _ = uic.loadUiType(os.path.join( os.path.dirname(__file__), 'Ui_Import.ui')) @@ -111,7 +117,7 @@ def initialize(self): parentItem.addChild(childItem) query.finish() else: - self.showQueryError(query) + self.xplanplugin.tools.showQueryError(query) query.finish() self.layerChooser.resizeColumnToContents(0) @@ -349,9 +355,6 @@ def initializeValues(self): self.fillBereichTree() - def debug(self, msg): - QtWidgets.QMessageBox.information(None, "Debug", msg) - def fillBereichTree(self): self.bereich.clear() planArt = self.planArt @@ -734,6 +737,476 @@ def accept(self): self.selected = item.id self.done(self.selected) + + + +class HoehenmanagerDialog(QtWidgets.QDialog, HOEHENMANAGER_CLASS): + + class featureSelector(QgsMapToolIdentifyFeature): + + def __init__(self, canvas, layer, manager): + super().__init__(canvas) + self.layer = layer + self.manager = manager + + def canvasReleaseEvent(self, e): + super().canvasReleaseEvent(e) + if e.button()==2: #rightclick + self.manager.canvas.contextMenu.exec_(e.globalPos()) + else: + ident_result = self.identify( + e.pixelPoint().x(), + e.pixelPoint().y(), + mode = self.IdentifyMode(3), + layerList= [self.layer] + ) + if len(ident_result)>0: + sfeature = ident_result[0].mFeature + self.manager.selectedFeature = sfeature + self.featureIdentified.emit(sfeature) + + def __init__(self, xplanplugin, hoehenLayer): + QtWidgets.QDialog.__init__(self) + # Set up the user interface from Designer. + self.setupUi(self) + self.xplanplugin = xplanplugin + self.hoehenLayer = hoehenLayer + self.selectedFeature = None + self.relator = None + + self.debug = self.xplanplugin.debug + + #canvas + refSchema = "BP_Basisobjekte" + refTable = "BP_Flaechenobjekte" #this is a view for qgis! + if list(self.xplanplugin.aktiveBereicheGids())==[]: + filter='' + else: + filter = self.xplanplugin.getBereichFilter(refSchema, refTable, self.xplanplugin.aktiveBereicheGids()) + + text_format = QgsTextFormat() + text_format.setFont(QFont("Liberation Mono", 10)) + text_format.setSize(10) + + setting = QgsPalLayerSettings() + setting.setFormat(text_format) + setting.fieldName = "gid" + setting.placement = 0 + setting.fitInPolygonOnly = 0 + setting.polygonPlacementFlags = 2 + setting.centroidWhole = 1 + setting.enabled = True + setting = QgsVectorLayerSimpleLabeling(setting) + + fsymb = QgsFillSymbol.createSimple({'color': '#c9c9c9', 'outline_color': 'black'}) + + self.flaechenobjekteLayer = self.xplanplugin.getQgisLayerForTable( + refSchema, + refTable, + geomColumn='position', + displayName = refTable + " (für Höheneditor - temporär)", + filterOnNew = filter + ) + self.flaechenobjekteLayer.setLabeling(setting) + self.flaechenobjekteLayer.setLabelsEnabled(True) + + renderer = self.flaechenobjekteLayer.renderer() + renderer.setSymbol(fsymb) + + self.flaechenobjekteLayer.triggerRepaint() + + linkSchema = "XP_Basisobjekte" + linkTable = "XP_Objekt_hoehenangabe" + self.linkLayer = self.xplanplugin.getDDCoveredLayerForTable(linkSchema, linkTable) + + self.canvas.setExtent(self.flaechenobjekteLayer.extent()) + self.canvas.setLayers([self.flaechenobjekteLayer]) + # QgsProject.instance().removeMapLayer(flaechenobjekteLayer) + self.canvas.setCanvasColor(QtCore.Qt.white) #does this work like this? + self.canvas.enableAntiAliasing(True) + + # create the map tools + # self.actionZoomIn = QAction("Hineinzoomen", self) + # self.actionZoomIn.triggered.connect(self.zoomIn) + # self.toolZoomIn = QgsMapToolZoom(self.canvas, False) # false = in + # self.toolZoomIn.setAction(self.actionZoomIn) + # + # self.actionZoomOut = QAction("Herauszoomen", self) + # self.actionZoomOut.triggered.connect(self.zoomOut) + # self.toolZoomOut = QgsMapToolZoom(self.canvas, True) # true = out + # self.toolZoomOut.setAction(self.actionZoomOut) + + self.actionPan = QAction("Pan", self) + self.actionPan.triggered.connect(self.pan) + self.toolPan = QgsMapToolPan(self.canvas) + self.toolPan.setAction(self.actionPan) + + self.actionDeselect = QAction("Auswahl aufheben", self) + self.actionDeselect.triggered.connect(self.deselectFlaeche) + + self.canvas.contextMenu = QtWidgets.QMenu(self.canvas) + self.canvas.contextMenu.addAction(self.actionDeselect) + # self.canvas.contextMenu.addAction(self.actionZoomIn) + # self.canvas.contextMenu.addAction(self.actionZoomOut) + + self.toolIdentify = self.featureSelector(self.canvas, self.flaechenobjekteLayer, self) + self.toolIdentify.featureIdentified.connect(self.fillRelatedHoehen) + + self.identify() + + + #!canvas + + self.hoehen.customContextMenuRequested.connect(self.on_hoehen_customContextMenuRequested) + self.hoehen.contextMenu = QtWidgets.QMenu(self.hoehen) + + self.editAction = QtWidgets.QAction("Bearbeiten", self.hoehen.contextMenu) + self.editAction.triggered.connect(self.editHoehen) + self.hoehen.contextMenu.addAction(self.editAction) + + self.removeAction = QtWidgets.QAction(u"Löschen", self.hoehen.contextMenu) + self.removeAction.triggered.connect(self.removeHoehen) + self.hoehen.contextMenu.addAction(self.removeAction) + + self.newAction = QtWidgets.QAction("Neue externeHoehen", self.hoehen.contextMenu) + self.newAction.triggered.connect(self.newHoehen) + self.hoehen.contextMenu.addAction(self.newAction) + + self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect(self.initialize) + + self.initialize() + + def deselectFlaeche(self): + self.flaechenobjekteLayer.removeSelection() + + def zoomIn(self): + self.canvas.setMapTool(self.toolZoomIn) + + def zoomOut(self): + self.canvas.setMapTool(self.toolZoomOut) + + def pan(self): + self.canvas.setMapTool(self.toolPan) + + def identify(self): + self.canvas.setMapTool(self.toolIdentify) + + def unselectFeature(self): + self.flaechenobjekteLayer.removeSelection() + + def initialize(self): + self.txlFilter.setText("") + #self.btnFilter.setEnabled(False) + self.setTitle = "Hoehenmanager" + self.fillHoehen() + + def getRelatorTable(self): + sql = """ +SELECT + a.id, + --a.*, + b."XP_Objekt_gid", + --c.uuid, c.gml_id, + --d.position as geom_poly, + --e.position as geom_line, + --f.position as geom_point, + g."gehoertZuBereich"--, + --h."nummer" as nr_bereich, h."name" as name_bereich +FROM +(select * from "XP_Sonstiges"."XP_Hoehenangabe") a + right join +(select * from "XP_Basisobjekte"."XP_Objekt_hoehenangabe") b +on a.id=b.hoehenangabe + left join +(select * from "XP_Basisobjekte"."XP_Objekt") c +on b."XP_Objekt_gid"=c.gid +-- left join +--(select * from "BP_Basisobjekte"."BP_Flaechenobjekte") d +--on c.gid=d.gid +-- left join +--(select * from "BP_Basisobjekte"."BP_Linienobjekte") e +--on c.gid=e.gid +-- left join +--(select * from "BP_Basisobjekte"."BP_Punktobjekte") f +--on c.gid=f.gid + left join +(select * from "XP_Basisobjekte"."XP_Objekt_gehoertZuBereich") g +on c.gid=g."XP_Objekt_gid" + left join +(select gid, nummer, name from "XP_Basisobjekte"."XP_Bereich") h +on g."gehoertZuBereich"=h.gid +""" + addendum = """where "gehoertZuBereich" = ANY( {}::int[] ); + """.format("'" + str(tuple(self.xplanplugin.aktiveBereicheGids())).replace('(','{').rsplit(',',1)[0] + "}'") + if (len(self.xplanplugin.aktiveBereicheGids())>0): + sql = sql + addendum + # self.showError(sql) + + if (self.relator is None): + query = QtSql.QSqlQuery(self.xplanplugin.db) + query.prepare(sql) + query.exec_() + + if query.isActive(): + table = {} + lastId = -1 + while query.next(): # returns false when all records are done + rec_dict = {} + for i in range(query.record().count()): + rec_dict[query.record().field(i).name()] = query.record().value(i) + table[query.record().value("id")] = rec_dict + lastId = query.record().value("id") + + query.finish() + self.relator = table, lastId + return self.relator + else: + self.xplanplugin.tools.showQueryError(query) + return None + else: + # self.showError('relator is loaded from cache') + return self.relator + + + def fillRelatedHoehen(self, sfeature=None): + """Code called when the feature is selected by the user""" + if sfeature is None: sfeature =self.selectedFeature + self.selectedFeature = sfeature + self.flaechenobjekteLayer.reload() + self.flaechenobjekteLayer.deselect(self.flaechenobjekteLayer.selectedFeatureIds()) + if not (sfeature is None): + self.flaechenobjekteLayer.select([sfeature.id()]) #id is correct + self.fillHoehen([sfeature['gid']]) #gid is correct + else: + self.fillHoehen() + + def fillHoehen(self, xp_objekt_gids =[]): + self.hoehen.clear() + filter = self.txlFilter.text() + + if filter == "": + self.hoehenLayer.removeSelection() + self.hoehenLayer.invertSelection() + else: + abfrage = "\"id\" like '%" + filter + "%'" + self.hoehenLayer.selectByExpression(abfrage) + + try: + relator, lastId = self.getRelatorTable() + except TypeError as e: + self.showError('An TypeError occured unexpected. Please fix this!') + relator = self.getRelatorTable() + relator_selector = {} + else: + if (xp_objekt_gids==[]): + relator_selector = relator + else: + relator_selector = {k: v for k, v in relator.items() if v['XP_Objekt_gid'] in xp_objekt_gids} + + for i, aFeat in enumerate(self.hoehenLayer.selectedFeatures()): + # self.showError('feat nr: ' + str(i)) + # self.showError('id: ' + str(aFeat["id"])) + if aFeat.id() > 0 and aFeat["id"] in relator_selector.keys(): + # if xp_objekt_gids!=[]: self.showError('found id: ' + str(aFeat["id"])) + anItem = QtWidgets.QListWidgetItem( + str( ( + aFeat["id"], + aFeat['hoehenbezug'], + aFeat['bezugspunkt'], + ( + aFeat['h'], + aFeat['hMin'], + aFeat['hMax'], + aFeat['hZwingend'] + ) + ) ) + ) + anItem.feature = aFeat + self.hoehen.addItem(anItem) + self.hoehen.sortItems() + + def editFeature(self, thisFeature): + # self.showError(str(thisFeature.id())) + # self.showError(str(thisFeature.attributes())) + # self.showError(str(self.hoehenLayer)) + try: + self.xplanplugin.app.xpManager.showFeatureForm(self.hoehenLayer, thisFeature) + except Exception as e: + self.showError(str(e)) + self.showInfo(str(thisFeature.attributeMap())) + self.showInfo(str(self.hoehenLayer.name())) + raise e + self.fillRelatedHoehen() + + def highlightFeature(self, thisFeature): + pass + + def editHoehen(self): + self.editFeature(self.hoehen.currentItem().feature) + + def newHoehen(self): + if self.xplanplugin.db == None: + self.showError(u"Es ist keine Datenbank verbunden") + self.done(0) + else: + if not (self.selectedFeature is None): + firstItem = next(self.hoehenLayer.getFeatures()) + # self.showError(str(dict(firstItem.attributeMap()))) + newFeat = QgsFeature(firstItem.fields(),-1) + if self.xplanplugin.tools.setEditable(self.hoehenLayer, True, self.xplanplugin.iface): + self.hoehenLayer.addFeature(newFeat) + # there should be a block on the db enabled here, so we really get our maxId + # not the one added by an other person simultaniously... + if self.hoehenLayer.commitChanges(): + try: + self.hoehenLayer.reload() + maxIdHoehe = self.xplanplugin.tools.getMaxGid(self.xplanplugin.db, "XP_Sonstiges", + "XP_Hoehenangabe", pkFieldName = "id") + expr = '"id" >= ' + str(maxIdHoehe) + self.hoehenLayer.selectByExpression(expr) + + if len(self.hoehenLayer.selectedFeatures()) == 1: + thisFeat = self.hoehenLayer.selectedFeatures()[0] + self.editFeature(thisFeat) + + if self.linkLayer != None: + maxId = self.xplanplugin.tools.getMaxGid(self.xplanplugin.db, "XP_Basisobjekte", + "XP_Objekt_hoehenangabe", pkFieldName = "XP_Objekt_gid") + + if not (maxId is None): #TODO: What happens on empty table? + newLink = self.xplanplugin.tools.createFeature(self.linkLayer) + newLink.setAttribute("XP_Objekt_gid", self.selectedFeature['gid']) + newLink.setAttribute("hoehenangabe", thisFeat['id']) + + if self.xplanplugin.tools.setEditable(self.linkLayer, True, self.xplanplugin.iface): + if self.linkLayer.addFeature(newLink): + + if self.linkLayer.commitChanges(): + try: + self.linkLayer.reload() + expr = '"XP_Objekt_gid" = ' + str(maxId) + self.linkLayer.selectByExpression(expr) + + if len(self.linkLayer.selectedFeatures()) >= 1: + self.showInfo(u"Zuordnung Höhe->Flächenobjekt wurde eingefügt") + # linkFeat = hoehenAngLayer.selectedFeatures()[0] + # self.editFeature(linkFeat) + else: + errortext = u"Neues Feature nicht gefunden! " + expr + self.showError(errortext) + raise Exception(errortext) + except Exception as e: + self.linkLayer.deleteFeature(newLink.id()) + self.linkLayer.commitChanges() + raise e + + else: + errortext = u"Kann in Tabelle " + refSchema + "." + refTable + \ + u" kein Feature einfügen!" + self.showError(errortext) + raise Exception(errortext) + else: + raise Exception('Die Datenbank scheint fehlerhaft zu sein, self.linkLayer ist None') + else: + self.showError(u"Zuerst muss ein Bezugsobjekt auf der Karte ausgewählt werden!") + except: + self.showError('Ein unerwarteter Fehler ist aufgetreten, das Hinzufügen der neuen Höhenangabe wurde zurückgenommen.') + self.hoehenLayer.deleteFeature(newFeat.id()) + self.hoehenLayer.commitChanges() + + def removeHoehen(self): + feat2remove = self.hoehen.currentItem().feature + + """Erst Link(s) zerstören...""" + if not (self.linkLayer is None): + expr = '"hoehenangabe" = ' + str(feat2remove.id()) + self.debug(expr) + self.linkLayer.selectByExpression(expr) + if self.xplanplugin.tools.setEditable(self.linkLayer, True, self.xplanplugin.iface): + if ( + (self.selectedFeature is None) or # makes sure entries with a 1:m relation can only be deleted, if no Flächenobjekt is selected. or + len(self.linkLayer.selectedFeatures()) == 1 #fallback: makes sure only entries with a 1:1 relation are deleted easily + ): + for link2remove in self.linkLayer.selectedFeatures(): + if self.linkLayer.deleteFeature(link2remove.id()): + if self.linkLayer.commitChanges(): + self.linkLayer.reload() + self.fillRelatedHoehen() + + """... dann die eigenentliche Hoehenangabe entfernen""" + if self.xplanplugin.tools.setEditable(self.hoehenLayer, True, self.xplanplugin.iface): + if self.hoehenLayer.deleteFeature(feat2remove.id()): + if self.hoehenLayer.commitChanges(): + self.fillRelatedHoehen() + else: + self.showError(u"Konnte Änderungen nicht speichern") + else: + self.showError(u"Konnte Hoehen nicht löschen") + else: + self.showError(u"Kann Layer " + self.hoehenLayer.name() + u" nicht editieren") + + else: + self.showError(u"Konnte Änderungen in der LinkTabelle nicht speichern") + else: + #todo: create button for deselect + self.showError('Die ausgewählte Höhenangabe wird von mehreren Flächenobjekten referenziert '+ + 'und kann aus Sicherheitsgründen nur gelöscht werden, wenn oben in der Karte '+ + 'kein Flächenobjekt ausgewählt ist.') + else: + self.showError(u"Kann Layer " + self.linkLayer.name() + u" nicht editieren") + else: + self.showError('') + + + + def showError(self, msg): + self.xplanplugin.tools.showError(msg) + + def showWarning(self, msg): + self.xplanplugin.tools.showWarning(msg) + + def showInfo(self, msg): + self.xplanplugin.tools.showInfo(msg) + + def debug(self, msg, stacksize=0): + self.xplanplugin.tools.debug(msg, stacksize=stacksize) + + @QtCore.pyqtSlot( str ) + def on_txlFilter_textChanged(self, currentText): + self.btnFilter.setEnabled(len(currentText) > 0) + + @QtCore.pyqtSlot( ) + def on_txlFilter_returnPressed(self): + if len(self.txlFilter.text()) > 3: + self.btnFilter.click() + + @QtCore.pyqtSlot( ) + def on_btnFilter_clicked(self): + self.fillHoehen() + + @QtCore.pyqtSlot( QtWidgets.QListWidgetItem ) + def on_hoehen_itemDoubleClicked(self, clickedItem): + self.editFeature(clickedItem.feature) + + @QtCore.pyqtSlot( QtWidgets.QListWidgetItem ) + def on_hoehen_itemClicked(self, clickedItem): + self.highlightFeature(clickedItem.feature) + + @QtCore.pyqtSlot( QtCore.QPoint) + def on_hoehen_customContextMenuRequested(self, atPoint): + clickedItem = self.hoehen.itemAt(atPoint) + self.editAction.setVisible(clickedItem != None) + self.removeAction.setVisible(clickedItem != None) + + if clickedItem != None: + self.hoehen.setCurrentItem(clickedItem) + + self.hoehen.contextMenu.resize(self.hoehen.contextMenu.sizeHint()) + self.hoehen.contextMenu.popup(self.hoehen.mapToGlobal(QtCore.QPoint(atPoint))) + + @QtCore.pyqtSlot() + def reject(self): + self.done(0) class ReferenzmanagerDialog(QtWidgets.QDialog, REFERENZMANAGER_CLASS): def __init__(self, xplanplugin, referenzenLayer): @@ -803,7 +1276,6 @@ def newReferenz(self): if maxId != None: newFeat = self.xplanplugin.tools.createFeature(extRefLayer) - if self.xplanplugin.tools.setEditable(extRefLayer, True, self.xplanplugin.iface): if extRefLayer.addFeature(newFeat): if extRefLayer.commitChanges(): @@ -822,10 +1294,10 @@ def newReferenz(self): u" kein Feature einfügen!") def removeReferenz(self): - feat2Remove = self.referenzen.currentItem().feature + feat2remove = self.referenzen.currentItem().feature if self.xplanplugin.tools.setEditable(self.referenzenLayer, True, self.xplanplugin.iface): - if self.referenzenLayer.deleteFeature(feat2Remove.id()): + if self.referenzenLayer.deleteFeature(feat2remove.id()): if self.referenzenLayer.commitChanges(): self.fillReferenzen() else: