You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QGIS 3.22.3自定义插件中QgsMapToolEmitPoint/QgsMapToolIdentifyFeature初始化后无响应问题求助

QGIS Plugin Development: Troubleshooting Unresponsive MapTool Subclasses

Hey, let's work through your QGIS plugin issue step by step. First, let's recap what you're trying to do: you're building a plugin with a dock widget button that triggers a map tool to select/identify features from the spots layer, then pass their attributes to a database function. You ran into problems where subclasses of QgsMapToolEmitPoint and QgsMapToolIdentifyFeature didn't respond to canvas clicks, but a QgsMapTool subclass worked—though you're unsure how to get features with it.

Let's start by looking at your code and the root causes of the unresponsive tools, then walk through solutions for both QgsMapToolIdentifyFeature (the easier path for your use case) and QgsMapTool if you need it.


Your Current Code Snippets

Button Connection Logic

def connect_the_buttons(self): 
    # get spots 
    b = self.dock.select_spots_tool 
    b.setEnabled(True) 
    b.setCheckable(True) 
    b.clicked.connect(self.getSpots) 

Button Click Handler

def getSpots(self, checked): 
    if checked: 
        spot_id_List.clear() 
        t = self.selectSpots(self.iface) 
        #t = gs.selectSpots(self.iface) 
        self.iface.mapCanvas().setMapTool(t) 
        QApplication.setOverrideCursor(Qt.CrossCursor) 
        spots = QgsProject.instance().mapLayersByName('spots')[0] 
        self.iface.setActiveLayer(spots) 
        tType = type(t) 
        log(f'what is t? >> {type(t)}') 
        log(f'{tType}') 
        log(f'type({t})') 
        log(f'{self.selectSpots}') 
    else: 
        log('not checked') 
        QApplication.restoreOverrideCursor() 
        spot_id_List.clear() 

Problematic MapTool Subclass

class selectSpots(QgsMapToolEmitPoint): 
    #canvasClicked = pyqtSignal('QgsPointXY') 
    # def __new__(cls, *args, **kwargs): 
    #     return super(selectSpots, cls).__new__(cls, *args, **kwargs) 
    def __init__(self, canvas): 
        log('selectSpots __init__ function is running...') 
        self.canvas = self.iface.mapCanvas() 
        #QgsMapTool.__init__(self, canvas) 
        #self.layer = self.iface.activeLayer() 
        #self.iface.canvasClicked.connect(self.id_spots) 
        #c = iface.mapCanvas() 
        #activeSpots = iface.activeLayer() 
        QgsMapToolEmitPoint.__init__(self, self.canvas) 
        #self.iface.layer().connect(self.getLayerId) 
        #self.iface.currentLayerChanged.connect(self.active_changed) 
        #QApplication.setOverrideCursor(Qt.CrossCursor) 
        
    # def getLayerId(self): 
    #     log('connected_to_click layer event') 
    
    # def id_spots(self): 
    #     log('this happend when i clicked') 
    def activate(self): 
        log('The activate method was called.') 
    def canvasDoubleClickEvent(self, e): 
        log('Double Clicked') 
        log(f'double click event: {e}') 
    
    # def active_changed(self, layer): 
    #     activeSpots.removeSelection() 
    #     spot_id_List.clear() 
    def canvasReleaseEvent(self, event): 
        log('canvas release event recorded') 
    def canvasPressEvent(self, event): 
        log('canvas press event recorded') 
        point = event.mapPoint() 
        self.canvasClicked.emit(point) 
        log(f'point: {point}') 
        # log(f'event: {event}') 
        #self.handle_spot_selection() 
        # log(f'on press: spot_id_List: {spot_id_List}') 
        # selectedLayer = self.iface.activeLayer() 
        # selection = selectedLayer.selectedFeatures() 
        #self.iface.actionSelect().trigger() 
        # for f in selection: 
        #     spot_id_List.append(f.attribute('id')) 
        # log(f'after press: spot_id_List: {spot_id_List}')

Why Your QgsMapToolEmitPoint Subclass Isn't Working

The main issue here is incorrect initialization:

  1. Your selectSpots class expects a canvas parameter, but you're passing self.iface when creating the tool in getSpots()—that's a mismatch.
  2. Inside __init__, you're trying to access self.iface but never initialize it, which would throw an error (though your log shows __init__ runs, so maybe you have it defined elsewhere, but it's still messy).
  3. You're redefining self.canvas instead of using the passed parameter, which breaks the parent class setup.

Solution 1: Use QgsMapToolIdentifyFeature (Best for Your Use Case)

QgsMapToolIdentifyFeature is built exactly for what you need: clicking to identify features on a specific layer. Here's a cleaned-up implementation:

from qgis.gui import QgsMapToolIdentifyFeature

class selectSpots(QgsMapToolIdentifyFeature):
    def __init__(self, iface):
        self.iface = iface
        canvas = iface.mapCanvas()
        # Initialize parent class with the canvas
        super().__init__(canvas)
        # Set the target layer (you can update this dynamically if needed)
        self.spots_layer = QgsProject.instance().mapLayersByName('spots')[0]
        self.setLayer(self.spots_layer)
        # Connect the featureIdentified signal to your handler
        self.featureIdentified.connect(self.on_feature_identified)

    def on_feature_identified(self, feature):
        # This runs every time a feature is clicked
        spot_id = feature['id']
        log(f'Identified feature with ID: {spot_id}')
        # Add the ID to your list or pass it to your database function
        spot_id_List.append(spot_id)

    def activate(self):
        # Optional: Add logic when the tool is activated
        log('Spot selection tool activated')
        super().activate() # Don't forget to call the parent activate method!

Then update your getSpots() method to initialize the tool correctly:

def getSpots(self, checked): 
    if checked: 
        spot_id_List.clear() 
        # Pass iface to the tool, not canvas
        t = self.selectSpots(self.iface) 
        self.iface.mapCanvas().setMapTool(t) 
        QApplication.setOverrideCursor(Qt.CrossCursor) 
        spots = QgsProject.instance().mapLayersByName('spots')[0] 
        self.iface.setActiveLayer(spots) 
        log(f'Activated tool type: {type(t)}')
    else: 
        log('Spot selection tool deactivated') 
        QApplication.restoreOverrideCursor() 
        spot_id_List.clear() 

Solution 2: Use QgsMapTool to Get Features

If you want to stick with QgsMapTool, you can manually query features at the click location using a search radius (to account for mouse precision). Here's how:

from qgis.gui import QgsMapTool
from qgis.core import QgsRectangle, QgsFeatureRequest

class selectSpots(QgsMapTool):
    def __init__(self, iface):
        self.iface = iface
        canvas = iface.mapCanvas()
        super().__init__(canvas)
        self.setCursor(Qt.CrossCursor)

    def canvasPressEvent(self, event):
        # Get the clicked point in map coordinates
        point = event.mapPoint()
        layer = self.iface.activeLayer()
        
        # Validate the active layer
        if not layer or layer.type() != QgsMapLayer.VectorLayer:
            log('No valid vector layer selected')
            return
        
        # Create a small search rectangle around the click (adjust radius as needed)
        pixel_radius = 5 # 5 pixels around the click
        map_radius = self.canvas().mapUnitsPerPixel() * pixel_radius
        search_rect = QgsRectangle(
            point.x() - map_radius,
            point.y() - map_radius,
            point.x() + map_radius,
            point.y() + map_radius
        )
        
        # Query features in the rectangle
        request = QgsFeatureRequest().setFilterRect(search_rect)
        for feature in layer.getFeatures(request):
            # Check if the point is inside the feature geometry
            if feature.geometry().contains(point):
                spot_id = feature['id']
                log(f'Selected feature ID: {spot_id}')
                spot_id_List.append(spot_id)
                break # Stop after finding the first matching feature

Key Tips to Avoid Future Issues

  • Always call the parent class's __init__ and activate methods: Skipping these breaks the core map tool functionality.
  • Match parameters when initializing classes: If your tool needs access to iface, pass it explicitly instead of relying on global variables.
  • Use built-in signals and tools: QGIS provides specialized map tools for most common tasks (like QgsMapToolIdentifyFeature for feature selection) so you don't have to reinvent the wheel.

内容的提问来源于stack exchange,提问作者Johnny D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:44:09