1

I am new to QGIS and trying different things. A Python script (link), which is running perfectly in QGIS Python console but I want it to run as a QGIS plugin. I am trying to do but getting confused about where to write my script logic in Plugin file. I copied the code below for reference which I want into Python plugin.

class TestEditTool(QgsMapToolEdit):

    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEdit.__init__(self, self.canvas)
        self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.rb.setStrokeColor(QColor('Red'))
        self.rb.setWidth(2.0)
        self.fixed_points = []

    def canvasReleaseEvent(self, event):
        click_point = event.snapPoint()
        if event.button() == Qt.LeftButton:
            if len(self.fixed_points) < 1:
                if not event.isSnapped():
                    iface.messageBar().pushMessage('Edit Error', 'Start point of line feature \
                    must be snapped to point layer', level=Qgis.Warning, duration=3)
                else:
                    self.fixed_points.append(QgsPoint(click_point))
            else:
                self.fixed_points.append(QgsPoint(click_point))
        elif event.button() == Qt.RightButton:
            if self.fixed_points:
                if not event.isSnapped():
                    iface.messageBar().pushMessage('Edit Error', 'End point of line feature \
                    must be snapped to point layer', level=Qgis.Warning, duration=3)
                else:
                    self.fixed_points.append(QgsPoint(click_point))
                    new_line = QgsLineString(self.fixed_points)
                    geom = QgsGeometry().fromPolyline(new_line)
                    ### Add Features here
                    layer = iface.activeLayer()
                    if layer.isEditable():
                        feat = QgsFeature()
                        feat.setGeometry(geom)
                        feat.setFields(layer.fields())
                        iface.openFeatureForm(layer, feat, False)
                        layer.addFeature(feat)
                        layer.triggerRepaint()
                    ###
                    self.clean_up()

    def canvasMoveEvent(self, event):
        if self.fixed_points:
            self.rb.reset()
            if len(self.fixed_points) == 1:
                pt1 = self.fixed_points[0]
                pt2 = QgsPoint(event.snapPoint())
                rb_line = QgsLineString(pt1, pt2)
                self.rb.setToGeometry(QgsGeometry().fromPolyline(rb_line), QgsProject().instance().crs())
            else:
                rb_line = QgsLineString(self.fixed_points)
                rb_line.addVertex(QgsPoint(event.snapPoint()))
                self.rb.setToGeometry(QgsGeometry().fromPolyline(rb_line), QgsProject().instance().crs())
            self.rb.show()

    def clean_up(self):
        self.rb.reset()
        self.fixed_points.clear()
4
  • 1
    the most simple way is use github.com/wonder-sk/qgis-minimal-plugin ,add your new class and put the methods inside the run method
    – Fran Raga
    May 5, 2020 at 11:10
  • @FranRaga My script includes class definition, in that case should I write my complete code inside run method ? May 5, 2020 at 11:15
  • not, you just import your class and in the run method you make the calls
    – Fran Raga
    May 5, 2020 at 13:43
  • @FranRaga, I tried doing same as you suggested, by importing my class in the run method. Plugin reload is successful but there is no result produced by the plugin after execution. May 5, 2020 at 16:39

1 Answer 1

2

This can be confusing for new users, and Plugin Builder, while a very useful tool and a great way to get started, can add to confusion with a lot of generated code. To help you out I have created a plugin incorporating the code in question. It is quite a minimal plugin, based on the example by Martin Dobias (wonder-sk) here, but it uses a separate plugin_name.py file instead of putting all the logic in the __init__.py file.

The contents of the main plugin file, add_snapped_feature.py looks like this:

"""
/****************************************************************************************
Copyright:  (C) Ben Wirf
Date:       May 2020
Email:      [email protected]
****************************************************************************************/
"""

from qgis.core import Qgis, QgsProject, QgsWkbTypes, QgsPoint, QgsLineString, QgsGeometry, QgsFeature
from qgis.gui import QgsMapToolEdit, QgsRubberBand
from PyQt5.QtWidgets import QAction, QToolBar, QMessageBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor


class AddSnappedFeature:

    def __init__(self, iface):
        self.iface = iface
        self.window = self.iface.mainWindow()
        self.canvas = self.iface.mapCanvas()
        self.toolbar = [c for c in self.window.children() if isinstance(c, QToolBar) and c.objectName() == 'mPluginToolBar'][0]
        self.action = QAction('Add Snapped Feature', self.window)

    def initGui(self):
        """This method is where we add the plugin action to the plugin toolbar.
        This is also where we connect any signals and slots
        such as Push Buttons to our class methods which contain our plugin logic."""
        self.action.setObjectName('btnGo')
        self.toolbar.addAction(self.action)
        self.action.triggered.connect(self.run)


    def run(self):
        t = TestEditTool(self.canvas, self.iface)
        self.canvas.setMapTool(t)


    def unload(self):
        self.toolbar.removeAction(self.action)
        del self.action

class TestEditTool(QgsMapToolEdit):

    def __init__(self, canvas, iface):
        self.canvas = canvas
        self.iface = iface
        QgsMapToolEdit.__init__(self, self.canvas)
        self.msg = QMessageBox()
        self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.rb.setStrokeColor(QColor('Red'))
        self.rb.setWidth(2.0)
        self.first_click = True
        self.fixed_points = []
        self.deactivated.connect(lambda: self.clean_up)

    def canvasReleaseEvent(self, event):
        click_point = event.snapPoint()
        if event.button() == Qt.LeftButton:
            if len(self.fixed_points) < 1:
                if not event.isSnapped():
                    self.iface.messageBar().pushMessage('Edit Error', 'Start point of line feature \
                    must be snapped to point layer', level=Qgis.Warning, duration=3)
                else:
                    self.fixed_points.append(QgsPoint(click_point))
            else:
                self.fixed_points.append(QgsPoint(click_point))
        elif event.button() == Qt.RightButton:
            if self.fixed_points:
                if not event.isSnapped():
                    self.iface.messageBar().pushMessage('Edit Error', 'End point of line feature \
                    must be snapped to point layer', level=Qgis.Warning, duration=3)
                else:
                    self.fixed_points.append(QgsPoint(click_point))
                    new_line = QgsLineString(self.fixed_points)
                    geom = QgsGeometry().fromPolyline(new_line)
                    ### Add Features here
                    layer = self.iface.activeLayer()
                    if layer.isEditable():
                        ###
                        layer.beginEditCommand('Add Snapped Feature')
                        feat = QgsFeature(layer.fields(), layer.featureCount())
                        feat.setGeometry(geom)
                        tbl = self.iface.openFeatureForm(layer, feat)
                        if tbl == True:
                            layer.dataProvider().addFeature(feat)
                            layer.endEditCommand()
                        elif tbl == False:
                            layer.destroyEditCommand()
                        ###
                        layer.triggerRepaint()
                    ###
                    self.clean_up()

    def canvasMoveEvent(self, event):
        if self.fixed_points:
            self.rb.reset()
            if len(self.fixed_points) == 1:
                pt1 = self.fixed_points[0]
                pt2 = QgsPoint(event.snapPoint())
                rb_line = QgsLineString(pt1, pt2)
                self.rb.setToGeometry(QgsGeometry().fromPolyline(rb_line), QgsProject().instance().crs())
            else:
                rb_line = QgsLineString(self.fixed_points)
                rb_line.addVertex(QgsPoint(event.snapPoint()))
                self.rb.setToGeometry(QgsGeometry().fromPolyline(rb_line), QgsProject().instance().crs())
            self.rb.show()

    def clean_up(self):
        self.rb.reset()
        self.fixed_points.clear()

You can find the rest of the plugin files in my github repository here.

Create an empty folder called 'add_snapped_feature' and copy the 3 files, add_snapped_feature.py, __init__.py, and metadata.txt into it. To install the plugin, just copy the folder into your local QGIS plugins folder or zip it up then install from zip via the plugin manager.

Note: When digitizing new features, your project (canvas) CRS must be the same as the line layer you are editing.

3
  • Sorry, Its my mistake not to mention the link. I am new to QGIS and trying various things. I found your answer link for my other question. I modified the question. Sorry. May 6, 2020 at 7:22
  • @Vikram Aundhekar, no problem- I edited my answer also. Did my answers help you with the plugin implementation and the layer editing?
    – Ben W
    May 6, 2020 at 7:45
  • Yes, your answer helped me with implementation. Thank You. May 10, 2020 at 14:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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