class ConflictDialog(WIDGET, BASE): LOCAL, REMOTE, DELETE = 1,2, 3 def __init__(self, conflicts): super(ConflictDialog, self).__init__(None, Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.solved = False self.resolvedConflicts = {} self.conflicts = conflicts self.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) self.zoomButton.clicked.connect(self.zoomToFullExtent) self.solveButton.clicked.connect(self.solve) self.conflictsTree.itemClicked.connect(self.treeItemClicked) self.attributesTable.cellClicked.connect(self.cellClicked) self.solveAllLocalButton.clicked.connect(self.solveAllLocal) self.solveAllRemoteButton.clicked.connect(self.solveAllRemote) self.solveLocalButton.clicked.connect(self.solveLocal) self.solveRemoteButton.clicked.connect(self.solveRemote) self.showRemoteCheck.stateChanged.connect(self.showGeoms) self.showLocalCheck.stateChanged.connect(self.showGeoms) self.lastSelectedItem = None self.currentPath = None self.currentConflict = None self.theirsLayer = None self.oursLayer = None settings = QSettings() horizontalLayout = QHBoxLayout() horizontalLayout.setSpacing(0) horizontalLayout.setMargin(0) self.mapCanvas = QgsMapCanvas() self.mapCanvas.setCanvasColor(Qt.white) self.mapCanvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type = bool)) self.mapCanvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type = bool)) self.mapCanvas.mapRenderer().setProjectionsEnabled(True) action = settings.value("/qgis/wheel_action", 0, type = float) zoomFactor = settings.value("/qgis/zoom_factor", 2, type = float) self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor) horizontalLayout.addWidget(self.mapCanvas) self.canvasWidget.setLayout(horizontalLayout) self.panTool = QgsMapToolPan(self.mapCanvas) self.mapCanvas.setMapTool(self.panTool) self.solveButton.setEnabled(False) self.solveLocalButton.setEnabled(False) self.solveRemoteButton.setEnabled(False) self.fillConflictsTree() def fillConflictsTree(self): topTreeItems = {} for c in self.conflicts: path = os.path.dirname(c.path) if path in topTreeItems: topItem = topTreeItems[path] else: topItem = QTreeWidgetItem() topItem.setText(0, path) topItem.setIcon(0, layerIcon) topTreeItems[path] = topItem conflictItem = ConflictItem(c) topItem.addChild(conflictItem) for item in list(topTreeItems.values()): self.conflictsTree.addTopLevelItem(item) def cellClicked(self, row, col): if col > 2: return value = self.attributesTable.item(row, col).value geoms = (self.oursgeom, self.theirsgeom) self.attributesTable.setItem(row, 4, ValueItem(value, False, geoms)); self.attributesTable.item(row, 0).setBackgroundColor(Qt.white); self.attributesTable.item(row, 1).setBackgroundColor(Qt.white); self.attributesTable.item(row, 2).setBackgroundColor(Qt.white); attrib = self.attributesTable.item(row, 3).text() if attrib in self.conflicted: self.conflicted.remove(attrib) self.updateSolveButton() self.showGeoms() def treeItemClicked(self): item = self.conflictsTree.selectedItems()[0] if self.lastSelectedItem == item: return if isinstance(item, ConflictItem): self.lastSelectedItem = item self.currentPath = item.conflict.path self.updateCurrentPath() self.solveLocalButton.setEnabled(True) self.solveRemoteButton.setEnabled(True) self.solveButton.setEnabled(False) def updateCurrentPath(self): self.solveButton.setEnabled(False) self.solveLocalButton.setEnabled(False) self.solveRemoteButton.setEnabled(False) self.cleanCanvas() self.showFeatureAttributes() self.createLayers() self.showGeoms() self.zoomToFullExtent() def zoomToFullExtent(self): layers = [lay.extent() for lay in self.mapCanvas.layers() if lay.type() == lay.VectorLayer] if layers: ext = layers[0] for layer in layers[1:]: ext.combineExtentWith(layer) self.mapCanvas.setExtent(ext) self.mapCanvas.refresh() def cleanCanvas(self): self.mapCanvas.setLayerSet([]) layers = [self.oursLayer, self.theirsLayer] for layer in layers: if layer is not None: QgsMapLayerRegistry.instance().removeMapLayer(layer.id()) self.oursLayer = None self.theirsLayer = None def solveAllRemote(self): ret = QMessageBox.warning(self, "Solve conflict", "Are you sure you want to solve all conflicts using the 'To merge' version?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes); if ret == QMessageBox.Yes: self.solved = True self.resolvedConflicts = {c.path:self.REMOTE for c in self.conflicts} self.close() def solveAllLocal(self): ret = QMessageBox.warning(self, "Solve conflict", "Are you sure you want to solve all conflict using the 'Local' version?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes); if ret == QMessageBox.Yes: self.solved = True self.resolvedConflicts = {c.path:self.LOCAL for c in self.conflicts} self.close() def _afterSolve(self, remove = True): if remove: parent = self.lastSelectedItem.parent() parent.removeChild(self.lastSelectedItem) self.lastSelectedItem = None if parent.childCount() == 0: self.conflictsTree.invisibleRootItem().removeChild(parent) if self.conflictsTree.topLevelItemCount() == 0: self.solved = True self.close() self.attributesTable.setRowCount(0) self.cleanCanvas() self.solveButton.setEnabled(False) self.solveLocalButton.setEnabled(False) self.solveRemoteButton.setEnabled(False) def solveLocal(self): self.resolvedConflicts[self.currentPath] = self.LOCAL self._afterSolve() def solveRemote(self): self.resolvedConflicts[self.currentPath] = self.REMOTE self._afterSolve() def solve(self): attribs = {} for i in range(self.attributesTable.rowCount()): value = self.attributesTable.item(i, 4).value name = str(self.attributesTable.item(i, 3).text()) attribs[name] = value self.resolvedConflicts[self.currentPath] = attribs self._afterSolve() def updateSolveButton(self): self.solveButton.setEnabled(len(self.conflicted) == 0) def showFeatureAttributes(self): conflictItem = self.lastSelectedItem self.oursgeom = None self.theirsgeom = None geoms = (self.oursgeom, self.theirsgeom) self.currentConflictedAttributes = [] attribs = list(conflictItem.origin.keys()) self.attributesTable.setRowCount(len(attribs)) self.conflicted = [] for idx, name in enumerate(attribs): font = QFont() font.setBold(True) font.setWeight(75) item = QTableWidgetItem(name) item.setFont(font) self.attributesTable.setItem(idx, 3, item); self.attributesTable.setItem(idx, 4, ValueItem(None, False)); try: values = (conflictItem.origin[name], conflictItem.local[name], conflictItem.remote[name]) except Exception: #Local has been deleted self._afterSolve(False) self.solveModifyAndDelete(conflictItem.conflict.path, self.REMOTE) return except TypeError: #Remote has been deleted self._afterSolve(False) self.solveModifyAndDelete(conflictItem.conflict.path,self.LOCAL) return try: geom = QgsGeometry.fromWkt(values[0]) except: geom = None if geom is not None: self.theirsgeom = QgsGeometry().fromWkt(values[1]) self.oursgeom = QgsGeometry.fromWkt(values[2]) geoms = (self.oursgeom, self.theirsgeom) ok = values[0] == values[1] or values[1] == values[2] or values[0] == values[2] for i, v in enumerate(values): self.attributesTable.setItem(idx, i, ValueItem(v, not ok, geoms)); if not ok: self.conflicted.append(name) else: if values[0] == values[1]: newvalue = values[2] else: newvalue = values[1] self.attributesTable.setItem(idx, 4, ValueItem(newvalue, False, geoms)); self.attributesTable.resizeRowsToContents() self.attributesTable.horizontalHeader().setMinimumSectionSize(150) self.attributesTable.horizontalHeader().setStretchLastSection(True) def solveModifyAndDelete(self, path, modified): msgBox = QMessageBox() msgBox.setText("The feature has been modified in one version and deleted in the other one.\n" "How do you want to solve the conflict?") msgBox.addButton(QPushButton('Modify'), QMessageBox.YesRole) msgBox.addButton(QPushButton('Delete'), QMessageBox.NoRole) msgBox.addButton(QPushButton('Cancel'), QMessageBox.RejectRole) ret = msgBox.exec_() if ret == 0: self.resolvedConflicts[path] = modified self._afterSolve() elif ret == 1: self.resolvedConflicts[path] = self.DELETE self._afterSolve() else: pass def createLayers(self): types = [("Point", ptOursStyle, ptTheirsStyle), ("LineString", lineOursStyle, lineTheirsStyle), ("Polygon", polygonOursStyle, polygonTheirsStyle)] if self.oursgeom is not None: geomtype = types[int(self.oursgeom.type())][0] style = types[int(self.oursgeom.type())][1] self.oursLayer = loadLayerNoCrsDialog(geomtype + "?crs=EPSG:4326", "ours", "memory") pr = self.oursLayer.dataProvider() feat = QgsFeature() feat.setGeometry(self.oursgeom) pr.addFeatures([feat]) self.oursLayer.loadNamedStyle(style) self.oursLayer.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(self.oursLayer, False) else: self.oursLayer = None if self.theirsgeom is not None: geomtype = types[int(self.theirsgeom.type())][0] style = types[int(self.theirsgeom.type())][2] self.theirsLayer = loadLayerNoCrsDialog(geomtype + "?crs=EPSG:4326", "theirs", "memory") pr = self.theirsLayer.dataProvider() feat = QgsFeature() feat.setGeometry(self.theirsgeom) pr.addFeatures([feat]) self.theirsLayer.loadNamedStyle(style) self.theirsLayer.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(self.theirsLayer, False) else: self.theirsLayer = None def showGeoms(self): checks = [self.showRemoteCheck, self.showLocalCheck] layers = [self.oursLayer, self.theirsLayer] toShow = [] for lay, chk in zip(layers, checks): if lay is not None and chk.isChecked(): toShow.append(lay) self.mapCanvas.setRenderFlag(False) self.mapCanvas.setLayerSet([QgsMapCanvasLayer(layer) for layer in toShow]) self.mapCanvas.setRenderFlag(True) def closeEvent(self, evnt): if not self.solved: ret = QMessageBox.warning(self, "Conflict resolution", "There are unsolved conflicts.\n" "Do you really want to exit and abort the sync operation?", QMessageBox.Yes | QMessageBox.No) if ret == QMessageBox.No: evnt.ignore() return self.cleanCanvas()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) self.first_flag = True self.setWindowTitle('PyQGIS') # 调整窗口大小 self.resize(800, 600) # 初始化图层树 vl = QVBoxLayout(self.dockWidgetContents) self.layerTreeView = QgsLayerTreeView(self) vl.addWidget(self.layerTreeView) # 初始化地图画布 self.mapCanvas = QgsMapCanvas(self) hl = QHBoxLayout(self.frame) hl.setContentsMargins(0, 0, 0, 0) hl.addWidget(self.mapCanvas) # 建立桥梁 self.model = QgsLayerTreeModel(PROJECT.layerTreeRoot(), self) self.model.setFlag(QgsLayerTreeModel.AllowNodeRename) self.model.setFlag(QgsLayerTreeModel.AllowNodeReorder) self.model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility) self.model.setFlag(QgsLayerTreeModel.ShowLegendAsTree) self.model.setAutoCollapseLegendNodes(10) self.layerTreeView.setModel(self.model) self.layerTreeBridge = QgsLayerTreeMapCanvasBridge( PROJECT.layerTreeRoot(), self.mapCanvas, self) # 显示经纬度 self.mapCanvas.xyCoordinates.connect(self.showLngLat) # 打开工程 self.actionOpen.triggered.connect(self.actionOpenTriggered) # 退出程序 self.actionQuit.triggered.connect(self.close) # 地图工具 # TODO:放大、缩小没有图标 self.actionPanTriggered() self.actionPan.triggered.connect(self.actionPanTriggered) self.actionZoomin.triggered.connect(self.actionZoomInTriggered) self.actionZoomout.triggered.connect(self.actionZoomOutTriggered) self.actionIdentity.triggered.connect(self.actionIdentifyTriggered) # 图层 self.actionShapefile.triggered.connect(self.actionShapefileTriggered) self.actionCsv.triggered.connect(self.actionCsvTriggered) self.actionPostGIS.triggered.connect(self.actionPostGISTriggered) self.actionWFS.triggered.connect(self.actionWFSTriggered) self.actionGeotiff.triggered.connect(self.actionGeotiffTriggered) self.actionXYZ.triggered.connect(self.actionXYZTriggered) # 绘图工具 self.actionPoint.triggered.connect(self.actionPointTriggered) self.actionLine.triggered.connect(self.actionLineTriggered) self.actionRectangle.triggered.connect(self.actionRectangleTriggered) self.actionPolygon.triggered.connect(self.actionPolygonTriggered) # 关于Qt self.actionAboutQt.triggered.connect( lambda: QMessageBox.aboutQt(self, '关于Qt')) self.actionAbout.triggered.connect( lambda: QMessageBox.about(self, '关于', 'PyQGIS二次开发')) # self.actionPan.triggered.connect(self.actionPanTriggered) # self.actionIdentify.triggered.connect(self.actionIdentifyTriggered) # 图层右键菜单 self.customMenuProvider = CustomMenuProvider(self.layerTreeView, self.mapCanvas) self.layerTreeView.setMenuProvider(self.customMenuProvider) # self.layerTreeRegistryBridge = QgsLayerTreeRegistryBridge(PROJECT.layerTreeRoot(), PROJECT, self) def actionOpenTriggered(self): """打开工程""" data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '工程文件(*.qgs , *.qgz)') if data_file: PROJECT.read(data_file) def actionPanTriggered(self): self.mapTool = QgsMapToolPan(self.mapCanvas) self.mapCanvas.setMapTool(self.mapTool) def actionZoomInTriggered(self): self.mapTool = QgsMapToolZoom(self.mapCanvas, False) self.mapCanvas.setMapTool(self.mapTool) def actionZoomOutTriggered(self): self.mapTool = QgsMapToolZoom(self.mapCanvas, True) self.mapCanvas.setMapTool(self.mapTool) def actionIdentifyTriggered(self): # 设置识别工具 self.identifyTool = QgsMapToolIdentifyFeature(self.mapCanvas) self.identifyTool.featureIdentified.connect(self.showFeatures) self.mapCanvas.setMapTool(self.identifyTool) # 设置需要识别的图层 layers = self.mapCanvas.layers() if layers: # 识别画布中第一个图层 self.identifyTool.setLayer(layers[0]) def showFeatures(self, feature): print(type(feature)) QMessageBox.information(self, '信息', ''.join(feature.attributes())) def actionAddGroupTriggered(self): PROJECT.layerTreeRoot().addGroup('group1') def actionShapefileTriggered(self): """打开shp""" data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '*.shp') if data_file: layer = QgsVectorLayer( data_file, os.path.splitext(os.path.basename(data_file))[0], "ogr") self.addLayer(layer) def actionCsvTriggered(self): """加载csv数据""" data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '*.csv') if data_file: # 去掉盘符,否则图层无效 data_file = os.path.splitdrive(data_file)[1] uri = f"file://{data_file}?delimiter=,&xField=x&yField=y" print(uri) layer = QgsVectorLayer(uri, "point", "delimitedtext") self.addLayer(layer) def actionPostGISTriggered(self): """加载postgis图层""" dialog = PostGISDialog(self) if dialog.exec_(): uri = QgsDataSourceUri() uri.setConnection(dialog.lineEditHost.text(), dialog.lineEditPort.text(), dialog.lineEditDatabase.text(), dialog.lineEditUsername.text(), dialog.lineEditPassword.text()) # lineEditGeometryColumn:根据实际情况,可能为:wkb_geometry、geometry、the_geom... uri.setDataSource("public", dialog.lineEditLayer.text(), dialog.lineEditGeometryColumn.text()) layer = QgsVectorLayer(uri.uri(False), dialog.lineEditLayer.text(), "postgres") self.addLayer(layer) def actionWFSTriggered(self): """加载天地图WFS图层""" uri = 'http://gisserver.tianditu.gov.cn/TDTService/wfs?' \ 'srsname=EPSG:4326&typename=TDTService:RESA&version=auto&request=GetFeature&service=WFS' layer = QgsVectorLayer(uri, "RESA", "WFS") self.addLayer(layer) def actionGeotiffTriggered(self): """加载geotiff""" data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '*.tif') if data_file: layer = QgsRasterLayer(data_file, os.path.basename(data_file)) self.addLayer(layer) def actionXYZTriggered(self): uri = 'type=xyz&' \ 'url=https://www.google.cn/maps/vt?lyrs=s@804%26gl=cn%26x={x}%26y={y}%26z={z}&' \ 'zmax=19&' \ 'zmin=0&' \ 'crs=EPSG3857' layer = QgsRasterLayer(uri, 'google', 'wms') self.addLayer(layer) def addLayer(self, layer): if layer.isValid(): if self.first_flag: self.mapCanvas.setDestinationCrs(layer.crs()) self.mapCanvas.setExtent(layer.extent()) self.first_flag = False PROJECT.addMapLayer(layer) layers = [layer ] + [PROJECT.mapLayer(i) for i in PROJECT.mapLayers()] self.mapCanvas.setLayers(layers) self.mapCanvas.refresh() else: print('图层无效.') def actionPointTriggered(self): self.pointTool = PointMapTool(self.mapCanvas) self.mapCanvas.setMapTool(self.pointTool) def actionLineTriggered(self): self.lineTool = LineMapTool(self.mapCanvas) self.mapCanvas.setMapTool(self.lineTool) def actionRectangleTriggered(self): self.rectangleTool = RectangleMapTool(self.mapCanvas) self.mapCanvas.setMapTool(self.rectangleTool) def actionPolygonTriggered(self): self.polygonTool = PolygonMapTool(self.mapCanvas) self.mapCanvas.setMapTool(self.polygonTool) def showLngLat(self, point): x = point.x() y = point.y() self.statusbar.showMessage(f'经度:{x}, 纬度:{y}')
class ConflictDialog(WIDGET, BASE): LOCAL, REMOTE, DELETE = 1, 2, 3 def __init__(self, conflicts): super(ConflictDialog, self).__init__(None, Qt.WindowSystemMenuHint | Qt.WindowTitleHint) self.solved = False self.resolvedConflicts = {} self.conflicts = conflicts self.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) self.zoomButton.clicked.connect(self.zoomToFullExtent) self.solveButton.clicked.connect(self.solve) self.conflictsTree.itemClicked.connect(self.treeItemClicked) self.attributesTable.cellClicked.connect(self.cellClicked) self.solveAllLocalButton.clicked.connect(self.solveAllLocal) self.solveAllRemoteButton.clicked.connect(self.solveAllRemote) self.solveLocalButton.clicked.connect(self.solveLocal) self.solveRemoteButton.clicked.connect(self.solveRemote) self.showRemoteCheck.stateChanged.connect(self.showGeoms) self.showLocalCheck.stateChanged.connect(self.showGeoms) self.lastSelectedItem = None self.currentPath = None self.currentConflict = None self.theirsLayer = None self.oursLayer = None settings = QSettings() horizontalLayout = QHBoxLayout() horizontalLayout.setSpacing(0) horizontalLayout.setMargin(0) self.mapCanvas = QgsMapCanvas() self.mapCanvas.setCanvasColor(Qt.white) self.mapCanvas.enableAntiAliasing( settings.value("/qgis/enable_anti_aliasing", False, type=bool)) self.mapCanvas.useImageToRender( settings.value("/qgis/use_qimage_to_render", False, type=bool)) self.mapCanvas.mapRenderer().setProjectionsEnabled(True) action = settings.value("/qgis/wheel_action", 0, type=float) zoomFactor = settings.value("/qgis/zoom_factor", 2, type=float) self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor) horizontalLayout.addWidget(self.mapCanvas) self.canvasWidget.setLayout(horizontalLayout) self.panTool = QgsMapToolPan(self.mapCanvas) self.mapCanvas.setMapTool(self.panTool) self.solveButton.setEnabled(False) self.solveLocalButton.setEnabled(False) self.solveRemoteButton.setEnabled(False) self.fillConflictsTree() def fillConflictsTree(self): topTreeItems = {} for c in self.conflicts: path = os.path.dirname(c.path) if path in topTreeItems: topItem = topTreeItems[path] else: topItem = QTreeWidgetItem() topItem.setText(0, path) topItem.setIcon(0, layerIcon) topTreeItems[path] = topItem conflictItem = ConflictItem(c) topItem.addChild(conflictItem) for item in list(topTreeItems.values()): self.conflictsTree.addTopLevelItem(item) def cellClicked(self, row, col): if col > 2: return value = self.attributesTable.item(row, col).value geoms = (self.oursgeom, self.theirsgeom) self.attributesTable.setItem(row, 4, ValueItem(value, False, geoms)) self.attributesTable.item(row, 0).setBackgroundColor(Qt.white) self.attributesTable.item(row, 1).setBackgroundColor(Qt.white) self.attributesTable.item(row, 2).setBackgroundColor(Qt.white) attrib = self.attributesTable.item(row, 3).text() if attrib in self.conflicted: self.conflicted.remove(attrib) self.updateSolveButton() self.showGeoms() def treeItemClicked(self): item = self.conflictsTree.selectedItems()[0] if self.lastSelectedItem == item: return if isinstance(item, ConflictItem): self.lastSelectedItem = item self.currentPath = item.conflict.path self.updateCurrentPath() self.solveLocalButton.setEnabled(True) self.solveRemoteButton.setEnabled(True) self.solveButton.setEnabled(False) def updateCurrentPath(self): self.solveButton.setEnabled(False) self.solveLocalButton.setEnabled(False) self.solveRemoteButton.setEnabled(False) self.cleanCanvas() self.showFeatureAttributes() self.createLayers() self.showGeoms() self.zoomToFullExtent() def zoomToFullExtent(self): layers = [ lay.extent() for lay in self.mapCanvas.layers() if lay.type() == lay.VectorLayer ] if layers: ext = layers[0] for layer in layers[1:]: ext.combineExtentWith(layer) self.mapCanvas.setExtent(ext) self.mapCanvas.refresh() def cleanCanvas(self): self.mapCanvas.setLayerSet([]) layers = [self.oursLayer, self.theirsLayer] for layer in layers: if layer is not None: QgsMapLayerRegistry.instance().removeMapLayer(layer.id()) self.oursLayer = None self.theirsLayer = None def solveAllRemote(self): ret = QMessageBox.warning( self, "Solve conflict", "Are you sure you want to solve all conflicts using the 'To merge' version?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: self.solved = True self.resolvedConflicts = { c.path: self.REMOTE for c in self.conflicts } self.close() def solveAllLocal(self): ret = QMessageBox.warning( self, "Solve conflict", "Are you sure you want to solve all conflict using the 'Local' version?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: self.solved = True self.resolvedConflicts = { c.path: self.LOCAL for c in self.conflicts } self.close() def _afterSolve(self, remove=True): if remove: parent = self.lastSelectedItem.parent() parent.removeChild(self.lastSelectedItem) self.lastSelectedItem = None if parent.childCount() == 0: self.conflictsTree.invisibleRootItem().removeChild(parent) if self.conflictsTree.topLevelItemCount() == 0: self.solved = True self.close() self.attributesTable.setRowCount(0) self.cleanCanvas() self.solveButton.setEnabled(False) self.solveLocalButton.setEnabled(False) self.solveRemoteButton.setEnabled(False) def solveLocal(self): self.resolvedConflicts[self.currentPath] = self.LOCAL self._afterSolve() def solveRemote(self): self.resolvedConflicts[self.currentPath] = self.REMOTE self._afterSolve() def solve(self): attribs = {} for i in range(self.attributesTable.rowCount()): value = self.attributesTable.item(i, 4).value name = str(self.attributesTable.item(i, 3).text()) attribs[name] = value self.resolvedConflicts[self.currentPath] = attribs self._afterSolve() def updateSolveButton(self): self.solveButton.setEnabled(len(self.conflicted) == 0) def showFeatureAttributes(self): conflictItem = self.lastSelectedItem self.oursgeom = None self.theirsgeom = None geoms = (self.oursgeom, self.theirsgeom) self.currentConflictedAttributes = [] attribs = list(conflictItem.origin.keys()) self.attributesTable.setRowCount(len(attribs)) self.conflicted = [] for idx, name in enumerate(attribs): font = QFont() font.setBold(True) font.setWeight(75) item = QTableWidgetItem(name) item.setFont(font) self.attributesTable.setItem(idx, 3, item) self.attributesTable.setItem(idx, 4, ValueItem(None, False)) try: values = (conflictItem.origin[name], conflictItem.local[name], conflictItem.remote[name]) except Exception: #Local has been deleted self._afterSolve(False) self.solveModifyAndDelete(conflictItem.conflict.path, self.REMOTE) return except TypeError: #Remote has been deleted self._afterSolve(False) self.solveModifyAndDelete(conflictItem.conflict.path, self.LOCAL) return try: geom = QgsGeometry.fromWkt(values[0]) except: geom = None if geom is not None: self.theirsgeom = QgsGeometry().fromWkt(values[1]) self.oursgeom = QgsGeometry.fromWkt(values[2]) geoms = (self.oursgeom, self.theirsgeom) ok = values[0] == values[1] or values[1] == values[2] or values[ 0] == values[2] for i, v in enumerate(values): self.attributesTable.setItem(idx, i, ValueItem(v, not ok, geoms)) if not ok: self.conflicted.append(name) else: if values[0] == values[1]: newvalue = values[2] else: newvalue = values[1] self.attributesTable.setItem(idx, 4, ValueItem(newvalue, False, geoms)) self.attributesTable.resizeRowsToContents() self.attributesTable.horizontalHeader().setMinimumSectionSize(150) self.attributesTable.horizontalHeader().setStretchLastSection(True) def solveModifyAndDelete(self, path, modified): msgBox = QMessageBox() msgBox.setText( "The feature has been modified in one version and deleted in the other one.\n" "How do you want to solve the conflict?") msgBox.addButton(QPushButton('Modify'), QMessageBox.YesRole) msgBox.addButton(QPushButton('Delete'), QMessageBox.NoRole) msgBox.addButton(QPushButton('Cancel'), QMessageBox.RejectRole) ret = msgBox.exec_() if ret == 0: self.resolvedConflicts[path] = modified self._afterSolve() elif ret == 1: self.resolvedConflicts[path] = self.DELETE self._afterSolve() else: pass def createLayers(self): types = [("Point", ptOursStyle, ptTheirsStyle), ("LineString", lineOursStyle, lineTheirsStyle), ("Polygon", polygonOursStyle, polygonTheirsStyle)] if self.oursgeom is not None: geomtype = types[int(self.oursgeom.type())][0] style = types[int(self.oursgeom.type())][1] self.oursLayer = loadLayerNoCrsDialog(geomtype + "?crs=EPSG:4326", "ours", "memory") pr = self.oursLayer.dataProvider() feat = QgsFeature() feat.setGeometry(self.oursgeom) pr.addFeatures([feat]) self.oursLayer.loadNamedStyle(style) self.oursLayer.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(self.oursLayer, False) else: self.oursLayer = None if self.theirsgeom is not None: geomtype = types[int(self.theirsgeom.type())][0] style = types[int(self.theirsgeom.type())][2] self.theirsLayer = loadLayerNoCrsDialog( geomtype + "?crs=EPSG:4326", "theirs", "memory") pr = self.theirsLayer.dataProvider() feat = QgsFeature() feat.setGeometry(self.theirsgeom) pr.addFeatures([feat]) self.theirsLayer.loadNamedStyle(style) self.theirsLayer.updateExtents() QgsMapLayerRegistry.instance().addMapLayer(self.theirsLayer, False) else: self.theirsLayer = None def showGeoms(self): checks = [self.showRemoteCheck, self.showLocalCheck] layers = [self.oursLayer, self.theirsLayer] toShow = [] for lay, chk in zip(layers, checks): if lay is not None and chk.isChecked(): toShow.append(lay) self.mapCanvas.setRenderFlag(False) self.mapCanvas.setLayerSet( [QgsMapCanvasLayer(layer) for layer in toShow]) self.mapCanvas.setRenderFlag(True) def closeEvent(self, evnt): if not self.solved: ret = QMessageBox.warning( self, "Conflict resolution", "There are unsolved conflicts.\n" "Do you really want to exit and abort the sync operation?", QMessageBox.Yes | QMessageBox.No) if ret == QMessageBox.No: evnt.ignore() return self.cleanCanvas()
class MirrorMap(QWidget): def __init__(self, parent=None, iface=None): QWidget.__init__(self, parent) # self.setAttribute(Qt.WA_DeleteOnClose) self.iface = iface self.layerId2canvasLayer = {} self.canvasLayers = [] self.setupUi() def closeEvent(self, event): self.scaleFactor.valueChanged.disconnect(self.onExtentsChanged) if not self.iface is None: self.iface.mapCanvas().extentsChanged.discconnect( self.onExtentsChanged) self.iface.mapCanvas().mapRenderer( ).destinationCrsChanged.disconnect(self.onCrsChanged) self.iface.mapCanvas().mapRenderer().mapUnitsChanged.disconnect( self.onCrsChanged) self.iface.mapCanvas().mapRenderer( ).hasCrsTransformEnabled.disconnect(self.onCrsTransformEnabled) QgsProject.instance().layerWillBeRemoved.disconnect(self.delLayer) self.iface.currentLayerChanged.disconnect(self.refreshLayerButtons) self.closed.emit() return QWidget.closeEvent(self, event) def setupUi(self): self.setObjectName("mirrormap") gridLayout = QGridLayout(self) gridLayout.setContentsMargins(0, 0, gridLayout.verticalSpacing(), gridLayout.verticalSpacing()) self.canvas = QgsMapCanvas(self) self.canvas.setCanvasColor(QColor(255, 255, 255)) settings = QSettings() gridLayout.addWidget(self.canvas, 0, 0, 1, 5) self.addLayerBtn = QToolButton(self) # self.addLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon ) # self.addLayerBtn.setText("Add current layer") self.addLayerBtn.setIcon(GuiUtils.get_icon("add.png")) self.addLayerBtn.clicked.connect(self.tool_add_layer) gridLayout.addWidget(self.addLayerBtn, 1, 0, 1, 1) self.delLayerBtn = QToolButton(self) # self.delLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon ) # self.delLayerBtn.setText("Remove current layer") self.delLayerBtn.setIcon(GuiUtils.get_icon("remove.png")) self.delLayerBtn.clicked.connect(self.tool_remove_layer) gridLayout.addWidget(self.delLayerBtn, 1, 1, 1, 1) self.renderCheck = QCheckBox("Render", self) self.renderCheck.toggled.connect(self.toggleRender) self.renderCheck.setChecked(True) gridLayout.addWidget(self.renderCheck, 1, 2, 1, 1) self.scaleFactorLabel = QLabel(self) self.scaleFactorLabel.setText("Scale factor:") self.scaleFactorLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) gridLayout.addWidget(self.scaleFactorLabel, 1, 3, 1, 1) self.scaleFactor = QDoubleSpinBox(self) self.scaleFactor.setMinimum(0.0) self.scaleFactor.setMaximum(1000.0) self.scaleFactor.setDecimals(3) self.scaleFactor.setValue(1) self.scaleFactor.setObjectName("scaleFactor") self.scaleFactor.setSingleStep(.05) gridLayout.addWidget(self.scaleFactor, 1, 4, 1, 1) self.scaleFactor.valueChanged.connect(self.onExtentsChanged) # Add a default pan tool self.toolPan = QgsMapToolPan(self.canvas) self.canvas.setMapTool(self.toolPan) self.scaleFactor.valueChanged.connect(self.onExtentsChanged) self.set_iface(self.iface) def toggleRender(self, enabled): self.canvas.setRenderFlag(enabled) def extent(self): """ :return: Current extents of the map canvas view. """ return self.canvas.extent() def canvas_layers(self): """ :return: Layers currently in the canvas. :rtype: list """ return self.canvasLayers def on_canvas_refreshed(self): """ """ self.refresh_layers() def tool_add_layer(self): self.addLayer() def tool_remove_layer(self): self.delLayer() def set_iface(self, iface): if iface is None: return self.iface = iface self.iface.mapCanvas().extentsChanged.connect(self.onExtentsChanged) # self.iface.mapCanvas().mapCanvasRefreshed.connect(self.on_canvas_refreshed) self.iface.mapCanvas().destinationCrsChanged.connect(self.onCrsChanged) QgsProject.instance().layerWillBeRemoved.connect(self.delLayer) self.iface.currentLayerChanged.connect(self.refreshLayerButtons) self.refreshLayerButtons() self.onExtentsChanged() self.onCrsChanged() def refresh_layers(self): """ Checks if the layers in the canvas list have already been added. If not, then add to the property viewer canvas. """ for ly in self.iface.mapCanvas().layers(): layer_id = self._layerId(ly) if layer_id not in self.layerId2canvasLayer: self.addLayer(layer_id) # QCoreApplication.processEvents(QEventLoop.ExcludeSocketNotifiers|QEventLoop.ExcludeUserInputEvents) def onExtentsChanged(self): prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) self.canvas.setExtent(self.iface.mapCanvas().extent()) self.canvas.zoomByFactor(self.scaleFactor.value()) # self.canvas.refresh() self.canvas.setRenderFlag(prevFlag) def onCrsChanged(self): self.canvas.setDestinationCrs( self.iface.mapCanvas().mapSettings().destinationCrs()) def refreshLayerButtons(self): layer = self.iface.activeLayer() isLayerSelected = layer != None hasLayer = False for l in self.canvas.layers(): if l == layer: hasLayer = True break self.addLayerBtn.setEnabled(isLayerSelected and not hasLayer) self.delLayerBtn.setEnabled(isLayerSelected and hasLayer) def getLayerSet(self): return [self._layerId(x.layer()) for x in self.canvasLayers] def setLayerSet(self, layerIds=None): prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) if layerIds == None: self.layerId2canvasLayer = {} self.canvasLayers = [] self.canvas.setLayers([]) else: for lid in layerIds: self.addLayer(lid) self.refreshLayerButtons() self.onExtentsChanged() self.canvas.setRenderFlag(prevFlag) def addLayer(self, layerId=None): if layerId == None: layer = self.iface.activeLayer() else: layer = QgsProject.instance().mapLayer(layerId) if layer == None: return prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) # add the layer to the map canvas layer set self.canvasLayers = [] id2cl_dict = {} for l in self.iface.mapCanvas().layers(): lid = self._layerId(l) if lid in self.layerId2canvasLayer: # previously added cl = self.layerId2canvasLayer[lid] elif l == layer: # Selected layer cl = layer else: continue id2cl_dict[lid] = cl self.canvasLayers.append(cl) self.layerId2canvasLayer = id2cl_dict self.canvas.setLayers(self.canvasLayers) self.refreshLayerButtons() self.onExtentsChanged() self.canvas.setRenderFlag(prevFlag) def delLayer(self, layerId=None): if layerId == None: layer = self.iface.activeLayer() if layer == None: return layerId = self._layerId(layer) # remove the layer from the map canvas layer set if layerId not in self.layerId2canvasLayer: return prevFlag = self.canvas.renderFlag() self.canvas.setRenderFlag(False) cl = self.layerId2canvasLayer[layerId] del self.layerId2canvasLayer[layerId] self.canvasLayers.remove(cl) self.canvas.setLayers(self.canvasLayers) self.refreshLayerButtons() self.onExtentsChanged() self.canvas.setRenderFlag(prevFlag) def _layerId(self, layer): if hasattr(layer, 'id'): return layer.id() return layer.getLayerID()
# raster layer world_wmts_url = "contextualWMSLegend=0&crs=EPSG:4326&dpiMode=7&"\ "featureCount=10&format=image/jpeg&layers=opengeo:countries&"\ "styles=&tileMatrixSet=EPSG:4326&"\ "url=http://suite.opengeo.org/geoserver/gwc/service/wmts?request%3DGetCapabilities" world_lyr = QgsRasterLayer(world_wmts_url, "Countries", 'wms') print(world_lyr.isValid()) # vector layer geojson_contributors = path.join( path.dirname(QgsApplication.developersMapFilePath()), 'contributors.json') layer = QgsVectorLayer(geojson_contributors, "QGIS Contributors", "ogr") layer.setLayerTransparency(35) print(layer.isValid()) # managing map canvas map_canvas = QgsMapCanvas(new_dialog) map_canvas.setMinimumSize(800, 600) map_canvas.mapRenderer().setDestinationCrs(crs_wgs84) QgsMapLayerRegistry.instance().addMapLayers([layer, world_lyr], 0) map_canvas_layer_list = [QgsMapCanvasLayer(layer), QgsMapCanvasLayer(world_lyr)] map_canvas.setExtent(layer.extent()) map_canvas.setLayerSet(map_canvas_layer_list) new_dialog.show() print(dir(map_canvas.map)) print(map_canvas.layers())