PyQGIS独立应用中QgsMapCanvas.itemAt无法识别自定义LineAnnotation子类
解决PyQGIS中QgsRubberBand子类无法被正确识别的问题
问题核心
你创建了QgsRubberBand的子类LineAnnotation,但调用QgsMapCanvas.itemAt()时返回的是父类QgsRubberBand类型,无法调用子类自定义方法,甚至交互时崩溃,本质原因有两点:
- 自定义实例未被显式添加到画布的item集合中,导致画布无法正确追踪该对象。
- 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
相关产品推荐
相关产品推荐

