qgis / QGIS

QGIS is a free, open source, cross platform (lin/win/mac) geographical information system (GIS)
https://qgis.org
GNU General Public License v2.0
10.65k stars 3.01k forks source link

Vertex tool has highlighting & selecting mismatch #59308

Open tylerchism opened 3 weeks ago

tylerchism commented 3 weeks ago

What is the bug or the crash?

vertex tool activated for a single polygon in one layer will sometimes trigger vertex highlighting with mouse hover activated for separate polygon in separate layer. The "correct" polygon does not respond to vertex clicks. The "wrong" polygon responds to vertex clicks, but the clicks activate a random vertex of the "correct" polygon.

A bare bones test script has been provided to reproduce this issue.

Steps to reproduce the issue

  1. Start with fresh Qgis instance and no background map (just for clean viewing) and run this script in the console:
    
    from qgis.PyQt.QtCore import QVariant, Qt
    from qgis.PyQt.QtWidgets import QMenu, QAction
    from qgis.core import (
    QgsProject,
    QgsVectorLayer,
    QgsField,
    QgsFeature,
    QgsGeometry,
    QgsPointXY,
    QgsFields,
    QgsWkbTypes,
    QgsRectangle,
    QgsFeatureRequest
    )
    from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge, QgsMapTool, QgsRubberBand
    from qgis.utils import iface

Remove existing layers for a clean environment

QgsProject.instance().removeAllMapLayers()

Create a temporary polygon layer

polygon_layer = QgsVectorLayer("Polygon?crs=EPSG:4326", "Test Polygons", "memory") pr = polygon_layer.dataProvider()

Add an ID field

pr.addAttributes([QgsField("id", QVariant.Int)]) polygon_layer.updateFields()

Define two polygons

polygon1 = QgsFeature() polygon1.setFields(polygon_layer.fields()) polygon1.setAttribute("id", 1) polygon1.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(-10, -10), QgsPointXY(0, -10), QgsPointXY(0, 0), QgsPointXY(-10, 0), QgsPointXY(-10, -10)]]))

polygon2 = QgsFeature() polygon2.setFields(polygon_layer.fields()) polygon2.setAttribute("id", 2) polygon2.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(10, 10), QgsPointXY(20, 10), QgsPointXY(20, 20), QgsPointXY(10, 20), QgsPointXY(10, 10)]]))

Add polygons to the layer

pr.addFeatures([polygon1, polygon2]) polygon_layer.updateExtents()

Add the layer to the project

QgsProject.instance().addMapLayer(polygon_layer)

Custom Map Tool

class RightClickEditTool(QgsMapTool): def init(self, canvas): super().init(canvas) self.canvas = canvas self.tmp_layer = None self.previous_tmp_layer = None

def canvasPressEvent(self, event):
    if event.button() == Qt.RightButton:
        # Get the map point where the user clicked
        point = self.toMapCoordinates(event.pos())

        # Identify features at the clicked point
        layer = polygon_layer  # Use our specific polygon layer
        if not layer:
            return

        features = []
        # Adjust the search radius as needed
        rect = QgsRectangle(point.x() - 0.0001, point.y() - 0.0001, point.x() + 0.0001, point.y() + 0.0001)
        request = QgsFeatureRequest().setFilterRect(rect)
        for feature in layer.getFeatures(request):
            features.append(feature)

        if features:
            # If features are found, show the context menu
            feature = features[0]  # Take the first feature
            self.showContextMenu(event, feature)

def showContextMenu(self, event, feature):
    menu = QMenu()
    edit_action = QAction("Edit Polygon", menu)
    edit_action.triggered.connect(lambda: self.editPolygon(feature))
    menu.addAction(edit_action)
    menu.exec_(event.globalPos())

def editPolygon(self, feature):
    # Remove previous temporary layer if it exists
    if self.previous_tmp_layer:
        if self.previous_tmp_layer.isEditable():
            self.previous_tmp_layer.commitChanges()
        QgsProject.instance().removeMapLayer(self.previous_tmp_layer.id())
        self.previous_tmp_layer = None

    # Create a temporary layer and copy the feature into it
    self.tmp_layer = QgsVectorLayer("Polygon?crs=EPSG:4326", "Temporary Edit Layer", "memory")
    pr = self.tmp_layer.dataProvider()
    pr.addAttributes(feature.fields())
    self.tmp_layer.updateFields()

    tmp_feature = QgsFeature()
    tmp_feature.setFields(feature.fields())
    tmp_feature.setGeometry(feature.geometry())
    tmp_feature.setAttributes(feature.attributes())

    pr.addFeatures([tmp_feature])
    self.tmp_layer.updateExtents()

    # Add the temporary layer to the project
    QgsProject.instance().addMapLayer(self.tmp_layer)

    # Start editing and activate the vertex tool
    self.tmp_layer.startEditing()
    iface.setActiveLayer(self.tmp_layer)
    iface.actionVertexTool().trigger()

    # Store the temporary layer to remove it later
    self.previous_tmp_layer = self.tmp_layer

def deactivate(self):
    # Clean up when the tool is deactivated
    if self.previous_tmp_layer:
        if self.previous_tmp_layer.isEditable():
            self.previous_tmp_layer.commitChanges()
        #QgsProject.instance().removeMapLayer(self.previous_tmp_layer.id())
        QgsProject.instance().layerTreeRoot().removeChildNode(self.previous_tmp_layer.id())
        self.previous_tmp_layer = None
    super().deactivate()

Instantiate and activate the custom map tool

canvas = iface.mapCanvas() edit_tool = RightClickEditTool(canvas) canvas.setMapTool(edit_tool)


2. A custom click tool will now be active and you can right click on one of the two polygons to select "edit polygon"
3. Click edit polygon and you will see it triggers the vertex tool for that polygon and mouse hovering will indicate normal behavior
4. Click the toggle editing icon to exit editing for that polygon
5. Right click the other polygon and see if mouse hovering shows normal vertex highlighting for that polygon
6. Toggle editing again and repeat steps jumping back and forth between polygons until the issue arises, it happens every time within ten tries.

### Versions

QGIS version
3.34.10-Prizren
QGIS code revision
113de9e1
Qt version
5.15.13
Python version
3.12.5
GDAL/OGR version
3.9.2
PROJ version
9.4.0
EPSG Registry database version
v11.004 (2024-02-24)
GEOS version
3.12.2-CAPI-1.18.2
SQLite version
3.45.1
PDAL version
2.6.3
PostgreSQL client version
16.2
SpatiaLite version
5.1.0
QWT version
6.2.0
QScintilla2 version
2.14.1
OS version
Windows 10 Version 2009

Active Python plugins
debug_vs
0.7
flight-planner-qgis
1.0.10
gml_db_browser
0.1
line-builder-qgis
2.2.12
MultipleLayerSelection
2.0
pluginbuilder3
3.2.1
plugin_reloader
0.9.1
postgis_geoprocessing
0.9
qgis-feature_class_manager
v2.12.01
QGIS3-getWKT
1.4
quick_map_services
0.19.29
db_manager
0.1.20
MetaSearch
0.3.6
processing
2.12.99

### Supported QGIS version

- [X] I'm running a supported QGIS version according to [the roadmap](https://www.qgis.org/en/site/getinvolved/development/roadmap.html#release-schedule).

### New profile

- [X] I tried with a new [QGIS profile](https://docs.qgis.org/latest/en/docs/user_manual/introduction/qgis_configuration.html#working-with-user-profiles)

### Additional context

_No response_
gd-33 commented 2 weeks ago

I've noticed this problem also. Your recreation of the issue from QGIS first principals here really highlights the issue. I'd like to know if this is actually a bug or if there is just a simple line of code which is missing which would sidestep this issue...