1

Below code can be use to get the X,Y Coordinate of clicked location .

class PrintSnappedPoint(QgsMapToolEmitPoint):

    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.l = iface.activeLayer()
        self.i = QgsSnapIndicator(self.canvas)
        self.u = self.canvas.snappingUtils()
        self.c = self.u.config()
        self.c.setEnabled(True)
        self.c.setMode(QgsSnappingConfig.AdvancedConfiguration)
        self.s = QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 25.00,
                    QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)


    def canvasMoveEvent(self, e):
        m = self.u.snapToMap(e.pos())
        self.i.setMatch(m)
      

    def canvasPressEvent(self, e):
        print("not called")
        transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:3111"),
                                   QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance())
        if self.i.match().type():
            pointxy = self.i.match().point()
        else:
            pointxy = None
        if pointxy:
            point = [pointxy.x(), pointxy.y()]
            print('Point snapped to vertex: {}'.format(point))
       
    def deactivate(self):
        self.s = QgsSnappingConfig.IndividualLayerSettings(False, QgsSnappingConfig.Vertex , 25.00,
                    QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)

canvas = iface.mapCanvas()
T = PrintSnappedPoint(canvas)
canvas.setMapTool(T)

I wanted to implement same code with bottom click in QGIS plugin and want to get X, Y location and add them into two line edits X Coordinate and Y Coordinate.

I added below code to run method but the canvasMoveEvent function is not being called and does not affect any thing in map canvas.

def run(self):

         """Run method that performs all the real work"""

         # Create the dialog with elements (after translation) and keep reference
         # Only create GUI ONCE in callback, so that it will only load when the plugin is started
         if self.first_start == True:
             self.first_start = False
             self.dlg = ChidoByougaDialog()

         #conneting to function
         
         self.dlg.pushButton_2.clicked.connect(self.get_wakaban_coords)

         # show the dialog
         self.dlg.show()
         self.dlg.exec_()




    def get_wakaban_coords(self):
        canvas = self.iface.mapCanvas()
        T = PrintSnappedPoint(canvas,self.iface)
        canvas.setMapTool(T)


class PrintSnappedPoint(QgsMapToolEmitPoint):

    def __init__(self, canvas,iface):
        self.iface = iface
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        print("come upto here")
        self.l = self.iface.activeLayer()
        self.i = QgsSnapIndicator(self.canvas)
        self.u = self.canvas.snappingUtils()
        self.c = self.u.config()
        self.c.setEnabled(True)
        self.c.setMode(QgsSnappingConfig.AdvancedConfiguration)
        self.s = QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 25.00,
                                                           QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)

        print("lower end")

    def canvasMoveEvent(self, e):
        print("come to mouse event")
        m = self.u.snapToMap(e.pos())
        self.i.setMatch(m)
        print("not here")

    def canvasPressEvent(self, e):
        print("not called")
        transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:3111"),
                                           QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance())
        if self.i.match().type():
            pointxy = self.i.match().point()
        else:
            pointxy = None
        if pointxy:
            point = [pointxy.x(), pointxy.y()]
            print('Point snapped to vertex: {}'.format(point))

    def deactivate(self):
        self.s = QgsSnappingConfig.IndividualLayerSettings(False, QgsSnappingConfig.Vertex, 25.00,
                                                           QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)

Is there any proper way to get location clicked by user and store in the Line Edit?

3
  • 1
    I am not in front of a computer right now to test but I would suggest: 1. Create references to iface and canvas objects as instance attributes in the __init__() method of your plugin class e.g. self.iface = iface and self.canvas = self.iface.mapCanvas(). 2. Also create the instance of the map tool class in the __init__() method e.g. self.T = printSnappedPoint(self.canvas, self.iface). Then in the method get_wakaban_points() just do: self.canvas.setMapTool(self.T). I will try to post an answer later.
    – Ben W
    Sep 15 at 2:36
  • @Ben thank you so much it worked perfectly. Just curious to know how to add those return X,Y into line Edit of UI dynamically.
    – Devenepali
    Sep 15 at 3:00
  • @Devenpali, no problem! I will try to make an answer later. The approach I would try is add a parent argument to the map tool class constructor, create a class attribute (self.parent = parent) and pass your plugin dialog (self.dlg) as the parent. You can then access the dialog widgets from inside the map tool class e.g. self.parent.line_edit.setText(str(point)) to replace the print statement.
    – Ben W
    Sep 15 at 8:34
1

Below is a complete, minimal, working plugin script which shows how to do what you what. I saved this file as __init__.py, placed it in a folder with an appropriate metadata.txt file and copied the folder into the QGIS plugins directory.

Note that to populate the line edit in the plugin dialog with the coordinates from mouse clicks on the map canvas, we need to add a parent argument to the map tool class constructor. Then when we instantiate the map tool class we pass in the plugin dialog (self.dlg) as the parent object. This allows us to access the dialog widgets from within the map tool class and use methods such as setText() when overriding the canvasPressEvent() method.

#-----------------------------------------------------------
# Copyright (C) 2021 Ben Wirf
#-----------------------------------------------------------
# Licensed under the terms of GNU GPL 2
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#---------------------------------------------------------------------


from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QAction, QDialog, QLabel, QLineEdit, QPushButton,
                                QGridLayout)
from qgis.core import QgsProject, QgsTolerance, QgsSnappingConfig, QgsCoordinateTransform
from qgis.gui import QgsMapToolEmitPoint, QgsSnapIndicator

def classFactory(iface):
    return GetSnappedMapPoint(iface)

class GetSnappedMapPoint:
    def __init__(self, iface):
        self.iface = iface
        self.dlg = MyDialog(self.iface)
        self.T = None

    def initGui(self):
        self.action = QAction('Launch', self.iface.mainWindow())
        self.action.triggered.connect(self.run)
        self.iface.addToolBarIcon(self.action)
        
        self.dlg.btn.clicked.connect(self.get_wakaban_coords)

    def unload(self):
        self.iface.removeToolBarIcon(self.action)
        del self.action

    def run(self):
        self.dlg.show()
        
    def get_wakaban_coords(self):
        self.T = PrintSnappedPoint(self.iface, self.dlg)
        self.iface.mapCanvas().setMapTool(self.T)
    

class MyDialog(QDialog):
    
    def __init__(self, iface):
        self.iface = iface
        QDialog.__init__(self)
        self.setGeometry(200, 200, 600, 200)
        self.lbl = QLabel('Point snapped to vertex:', self)
        self.line_edit = QLineEdit(self)
        self.btn = QPushButton('Set map tool', self)
        self.layout = QGridLayout()
        self.layout.addWidget(self.lbl, 0, 0, 1, 1, Qt.AlignCenter)
        self.layout.addWidget(self.line_edit, 0, 1, 1, 2)
        self.layout.addWidget(self.btn, 1, 1, 1, 1)
        self.setLayout(self.layout)
        
        self.setWindowFlags(Qt.WindowStaysOnTopHint)
        
    def closeEvent(self, e):
        self.line_edit.clear()
        self.iface.actionPan().trigger()


class PrintSnappedPoint(QgsMapToolEmitPoint):

    def __init__(self, iface, parent):
        #Note constructor takes a parent argument, a reference to
        #which is stored as self.parent
        self.iface = iface
        self.parent = parent
        self.canvas = self.iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.l = self.iface.activeLayer()
        self.i = QgsSnapIndicator(self.canvas)
        self.u = self.canvas.snappingUtils()
        self.c = self.u.config()
        self.c.setEnabled(True)
        self.c.setMode(QgsSnappingConfig.AdvancedConfiguration)
        self.s = QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 25.00,
                                                           QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)

    def canvasMoveEvent(self, e):
        m = self.u.snapToMap(e.pos())
        self.i.setMatch(m)

    def canvasPressEvent(self, e):
        if self.i.match().type():
            pointxy = self.i.match().point()
        else:
            pointxy = None
        if pointxy:
            if self.l.crs() != QgsProject.instance().crs():
                x_form = QgsCoordinateTransform(QgsProject.instance().crs(),
                                                self.l.crs(),
                                                QgsProject.instance())
                tr = x_form.transform(pointxy)
                point = [tr.x(), tr.y()]
            else:
                point = [pointxy.x(), pointxy.y()]
            #Here we can access widgets in the plugin dialog
            #using the self.parent reference
            self.parent.line_edit.clear()
            self.parent.line_edit.setText('{}'.format(point))

    def deactivate(self):
        self.s = QgsSnappingConfig.IndividualLayerSettings(False, QgsSnappingConfig.Vertex, 25.00,
                                                           QgsTolerance.Pixels)
        self.c.setIndividualLayerSettings(self.l, self.s)
        self.u.setConfig(self.c)

You can see how it works in the short screencast below:

enter image description here

You can find a minimal QGIS plugin template in this very useful resource from Martin Dobias here:

https://github.com/wonder-sk/qgis-minimal-plugin

2
  • thank you so much works perfectly as always.
    – Devenepali
    Sep 22 at 4:14
  • @Devenepali, you're welcome :-)
    – Ben W
    Sep 23 at 6:57

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.