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

PyQGIS独立应用中QgsMapCanvas.itemAt无法识别自定义LineAnnotation子类

解决PyQGIS中QgsRubberBand子类无法被正确识别的问题

问题核心

你创建了QgsRubberBand的子类LineAnnotation,但调用QgsMapCanvas.itemAt()时返回的是父类QgsRubberBand类型,无法调用子类自定义方法,甚至交互时崩溃,本质原因有两点:

  1. 自定义实例未被显式添加到画布的item集合中,导致画布无法正确追踪该对象。
  2. QGIS的C++底层对象包装机制会丢失Python子类的类型信息,直接用type()无法识别子类。

解决方案

1. 显式添加自定义RubberBand到画布

创建LineAnnotation实例后,必须调用QgsMapCanvas.addItem()将其加入画布的item管理体系,否则画布无法正确识别和交互该对象。

2. 添加自定义标识属性区分子类

由于Python子类信息在C++层包装中丢失,我们可以给LineAnnotation类添加一个唯一标识属性,比如is_line_annotation,用于后续判断对象类型。

3. 安全的类型判断与交互

获取item后,先检查是否存在自定义标识属性,再调用子类方法,避免因类型错误导致崩溃。

修改后的完整代码

from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge, QgsRubberBand
from qgis.core import QgsApplication, QgsProject, QgsPointXY, QgsGeometry
from qgis.PyQt.QtGui import QColor
import sys

class LineAnnotation(QgsRubberBand):
    def __init__(self, canvas):
        super().__init__(canvas)  # 使用更规范的继承初始化方式
        self.setColor(QColor("red"))
        self.setWidth(10)
        self.is_line_annotation = True  # 添加自定义标识属性
        # 示例自定义方法
        def custom_method(self):
            print("调用了LineAnnotation的自定义方法")

class Interface(QgsMapCanvas):
    def __init__(self):
        super().__init__()
        self.setCanvasColor(QColor("#182F36"))
        project_path = "project_path"

        project = QgsProject.instance()
        project.read(project_path)
        layer_tree = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas=self)
        layer_tree.setAutoSetupOnFirstLayer(False)
        self.zoomToFeatureExtent(project.mapLayersByName('layer_name')[0].extent())
        self.enableAntiAliasing(True)
        self.setAcceptDrops(True)
        self.setParallelRenderingEnabled(True)

        p1 = QgsPointXY(524670.46860305720474571, 5470375.41737424582242966)
        p2 = QgsPointXY(589864.10151600651443005, 5487531.63656186405569315)

        r = LineAnnotation(self)
        r.setToGeometry(QgsGeometry.fromPolylineXY([p1, p2]))
        self.addItem(r)  # 关键:将自定义实例添加到画布

    def mousePressEvent(self, event) -> None:
        item = self.itemAt(event.pos())
        if item and hasattr(item, 'is_line_annotation') and item.is_line_annotation:
            print(f"识别到LineAnnotation实例,类型:{type(item)}")
            # 安全调用自定义方法
            item.custom_method()
        else:
            print(f"未识别到自定义实例,类型:{type(item)}")

class StackOverflow:
    def __init__(self):
        qgs = QgsApplication([], True)
        qgs.setDoubleClickInterval(250)
        qgs.initQgis()

        graphicUI = Interface()
        graphicUI.showMaximized()

        sys.exit(qgs.exec_())

if __name__ == '__main__':
    app = StackOverflow()

关键修改点说明

  • 使用super().__init__()替代直接调用父类构造函数,符合Python继承规范。
  • 给LineAnnotation添加is_line_annotation标识属性,用于绕过C++层的类型信息丢失问题。
  • 创建实例后调用self.addItem(r),将自定义RubberBand正式加入画布管理体系,解决交互崩溃问题。
  • 在mousePressEvent中通过检查属性来识别子类实例,确保安全调用自定义方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:50:17