PySide6-QtQuick中QQuickItem.updatePaintNode使用QSGGeometryNode无显示或崩溃问题求助
PySide6-QtQuick中QQuickItem.updatePaintNode使用QSGGeometryNode无显示或崩溃问题求助
问题背景
我在Ubuntu环境下使用Python3.12 + PySide6.9开发QtQuick应用,尝试通过QQuickItem自定义可在QML中使用的场景图节点。当返回QSGSimpleRectNode时一切正常,但换成QSGGeometryNode后,要么完全看不到渲染内容,要么直接触发内存访问错误(生成内存转储)。
工作的参考代码(QSGSimpleRectNode)
QML_IMPORT_NAME = "Instance2D" QML_IMPORT_MAJOR_VERSION = 1 from PySide6.QtQuick import (QQuickItem, QmlElement, QSGSimpleRectNode, QSGNode) from PySide6.QtGui import QColor, Qt, QRectF @QmlElement class Instance2D(QQuickItem): itemChanged = Signal(QObject, name="itemChanged") def __init__(self, parent=None): super().__init__(parent) self.setFlag(QQuickItem.Flag.ItemHasContents, True) def updatePaintNode(self, node: QSGNode, upd): if node is None: node = QSGSimpleRectNode() node.setColor(Qt.GlobalColor.green) node.setRect(QRectF(25, 25, 200, 500)) return node
这段代码可以正常渲染出绿色矩形,没有任何问题。
无法正常运行的代码(QSGGeometryNode)
# 其他代码和上面一致,仅修改updatePaintNode方法 def updatePaintNode(self, node: QSGNode, upd): if node is None: geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), 3) geometry.setDrawingMode(QSGGeometry.DrawingMode.DrawTriangles) geometry.allocate(3) # 不确定是否需要这行 material = QSGFlatColorMaterial() material.setColor(QColor(100, 255, 0)) node = QSGGeometryNode() node.setGeometry(geometry) node.setMaterial(material) node.setFlag(QSGNode.Flag.OwnsGeometry, True) node.setFlag(QSGNode.Flag.OwnsMaterial, True) vertices = geometry.vertexDataAsPoint2D() vertices[0].set(50, 50) vertices[1].set(100, 50) vertices[2].set(75, 120) # node.markDirty(QSGGeometryNode.DirtyStateBit.DirtyGeometry) # 尝试过但无效 return node
运行这段代码时,要么看不到任何图形,要么直接崩溃,没有明显的报错日志(除了内存访问错误)。
问题分析与解决方案
经过排查,你的QSGGeometryNode代码存在几个关键问题,导致了渲染失败或崩溃:
1. 内存管理与顶点数据操作问题
- 你调用了多余的
geometry.allocate(3):QSGGeometry在初始化时已经通过QSGGeometry(defaultAttributes_Point2D(), 3)分配了3个顶点的内存,重复调用allocate()会打乱内存布局,引发访问错误。 - 顶点数据的设置时机不对:你应该在设置Geometry到Node之前就完成顶点坐标的赋值,并且确保操作的是可写内存。
2. 缺少必要的脏标记
即使你创建了正确的Geometry和Material,场景图可能不知道需要更新渲染数据,需要显式标记Geometry为脏。
3. 坐标范围问题
你设置的顶点坐标可能超出了QQuickItem的默认可视区域,导致图形被裁剪或完全不可见。
修正后的可运行代码
def updatePaintNode(self, node: QSGNode, upd): if node is None: # 初始化3个顶点的Geometry,指定绘制模式为三角形 geometry = QSGGeometry(QSGGeometry.defaultAttributes_Point2D(), 3) geometry.setDrawingMode(QSGGeometry.DrawingMode.DrawTriangles) # 获取可写的顶点数据指针,设置三角形的三个顶点坐标 vertices = geometry.vertexDataAsPoint2D() vertices[0].set(50, 50) vertices[1].set(250, 50) vertices[2].set(150, 250) # 初始化纯色材质 material = QSGFlatColorMaterial() material.setColor(QColor(100, 255, 0)) # 创建GeometryNode并设置属性 node = QSGGeometryNode() node.setGeometry(geometry) node.setMaterial(material) # 让Node自动管理Geometry和Material的内存 node.setFlag(QSGNode.Flag.OwnsGeometry, True) node.setFlag(QSGNode.Flag.OwnsMaterial, True) # 标记Geometry为脏,通知场景图更新渲染 node.markDirty(QSGNode.DirtyGeometry) return node
额外注意事项
- 确保在QML中给
Instance2D设置合适的宽高,比如:
Instance2D { width: 300 height: 300 anchors.centerIn: parent }
如果Item的宽高为默认的0,即使Geometry有坐标也会完全不可见。
- 避免在
updatePaintNode之外操作场景图节点,所有场景图相关的操作必须在这个函数的上下文中完成,因为场景图是在单独的线程中渲染的。
内容来源于stack exchange
相关产品推荐
相关产品推荐

