3

I would like to construct a map tool application that allows only to add some point features.

How do I complete the following code to activate snap mode while editing a layer with PyQGIS 3?

from qgis.core import *
from qgis.gui import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class AddPointTool(QgsMapTool):
   def __init__(self, mapCanvas, layer):
       QgsMapTool.__init__(self, mapCanvas)
       self.layer=layer
       self.setCursor(Qt.CrossCursor)

   def canvasReleaseEvent(self, event):
       point=self.toLayerCoordinates(self.layer, event.pos())
       fields=self.layer.dataProvider().fields()
       feature=QgsFeature()
       feature.setFields(fields)
       feature.setGeometry(QgsGeometry.fromPointXY(point))

       self.layer.addFeature(feature)
       self.layer.updateExtents()
       self.layer.triggerRepaint()

canvas=iface.mapCanvas()
layer=iface.activeLayer()

layer.startEditing()
layer.triggerRepaint()
tool=AddPointTool(canvas,layer)
canvas.setMapTool(tool)
  • The following expression enable snapping for PyQGIS 2.0 : QgsProject.instance().setSnapSettingsForLayer(layer.id(), True, 2, 1, 10, True) What is the equivalent expression in PyQGIS 3.0? – ennine Sep 13 '18 at 15:23
  • did you find any solution ? I would be interested ! – Svhooren Nov 15 '19 at 9:56
0

I created my own subclass of MapTool in order to solve the snapping issue. I subclassed QgsMapToolEmitPoint because I use the canvasClicked signal, but other MapTool classes can be simply adapted.

The key point is that instead of other examples I found out on the web, this solution uses the snapping settings of the tool's QgsMapCanvas (by means of self.canvas.snappingUtils()), so it simply listens to the user's snapping settings in the current working session (including Snapping On/Off, Snap to current/all Layers, Snap to Vertices/Segments/Both, Snap to Intersections On/Off, Snapping Radius, Snapping Indicator Color, ...).

class SnappingMapToolEmitPoint(QgsMapToolEmitPoint):
    snapClicked = pyqtSignal(QgsPointXY, Qt.MouseButton)

    def __init__(self, canvas):
        super().__init__(canvas)
        self.canvas = canvas
        self.canvasClicked.connect(self.snapClick)
        self.snapIndicator = QgsSnapIndicator(canvas)
        self.snapper = self.canvas.snappingUtils()

    def canvasMoveEvent(self, e):
        snapMatch = self.snapper.snapToMap(e.pos())
        self.snapIndicator.setMatch(snapMatch)

    @pyqtSlot(QgsPointXY, Qt.MouseButton)
    def snapClick(self, point, button):
        if self.snapIndicator.match().type():
            point = self.snapIndicator.match().point()
        self.snapClicked.emit(point, button)

With an instance of this class, I connect the snapClicked signal to the function I want to use. It works just like the canvasClicked signal of QgsMapToolEmitPoint, but returns a snapped point if snapping is enabled.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.