PySide6中Qt3D绘制3D线条失败及动态添加点问题求助
问题
- 在Qt6/PySide6的Qt3D模块中绘制基础3D线条失败,参考旧Qt5 C++代码改写后窗口为空,线条未渲染
- 核心需求:展示3D空间中运动物体的路径,支持初始化后从IMU持续数据流动态添加路径点
- 运行代码时出现两个警告:
Qt3D.Renderer.RHI.Backend: Initializing RHI with DirectX backendfindBoundingVolumeComputeData: Position attribute not suited for bounding volume computation
- 测试发现
__init__()执行后几何属性丢失,将其赋值给self会触发未授权内存访问崩溃
用户提供的原代码
from PySide6.QtCore import QTimer, QByteArray from PySide6.QtGui import QVector3D, QColor from PySide6.QtWidgets import QApplication, QMainWindow, QWidget from PySide6.Qt3DCore import Qt3DCore from PySide6.Qt3DExtras import Qt3DExtras from PySide6.Qt3DRender import Qt3DRender import struct class MainWindow(QMainWindow): def __init__(self): super().__init__() # Set up the 3D view self.view = Qt3DExtras.Qt3DWindow() self.view.defaultFrameGraph().setClearColor(QColor(255,255,255)) self.container = QWidget.createWindowContainer(self.view) self.setCentralWidget(self.container) # Set up the 3D scene self.root = Qt3DCore.QEntity() self.view.setRootEntity(self.root) # Create a 3D entity for the line self.line_entity = Qt3DCore.QEntity(self.root) line_geometry = Qt3DCore.QGeometry(self.line_entity) # Create a custom line geometry vertex_data = [QVector3D(0.0, 0.0, 0.0), QVector3D(1.0, 0.0, 0.0), QVector3D(1.0, 1.0, 0.0)] vertex_byteArray = QByteArray() for vector in vertex_data: vertex_byteArray.append(struct.pack('fff', vector.x(), vector.y(), vector.z())) vertex_buffer = Qt3DCore.QBuffer(line_geometry) vertex_buffer.setData(vertex_byteArray) # Create a QAttribute to store the vertex data vertex_attribute = Qt3DCore.QAttribute() vertex_attribute.setName(Qt3DCore.QAttribute.defaultPositionAttributeName()) vertex_attribute.setAttributeType(Qt3DCore.QAttribute.AttributeType.VertexAttribute) vertex_attribute.setVertexBaseType(Qt3DCore.QAttribute.VertexBaseType.Float) vertex_attribute.setBuffer(vertex_buffer) vertex_attribute.setVertexSize(3) # 3 components (X, Y, Z) per vertex vertex_attribute.setByteOffset(0) vertex_attribute.setByteStride(3 * 4) # 3 components * 4 bytes for float vertex_attribute.setCount(len(vertex_data)) line_geometry.addAttribute(vertex_attribute) # Create connections between vertices index_data = [0,1,2] index_bytes = QByteArray() index_bytes.append(bytearray(index_data)) index_buffer = Qt3DCore.QBuffer(line_geometry) index_buffer.setData(index_bytes) # Create a QAttribute to store the index data index_attribute = Qt3DCore.QAttribute() # index_attribute.setName(Qt3DCore.QAttribute.defaultJointIndicesAttributeName()) index_attribute.setAttributeType(Qt3DCore.QAttribute.AttributeType.IndexAttribute) index_attribute.setVertexBaseType(Qt3DCore.QAttribute.VertexBaseType.UnsignedInt) index_attribute.setBuffer(index_buffer) index_attribute.setCount(len(index_data)) line_geometry.addAttribute(index_attribute) # Create a line material self.line_material = Qt3DExtras.QPhongMaterial(self.root) self.line_material.setAmbient(QColor(255,0,0)) # Create a line renderer self.line_renderer = Qt3DRender.QGeometryRenderer(self.root) self.line_renderer.setPrimitiveType(Qt3DRender.QGeometryRenderer.LineStrip) self.line_renderer.setGeometry(line_geometry) self.line_renderer.setVertexCount(len(vertex_data)) self.line_entity.addComponent(self.line_renderer) self.line_entity.addComponent(self.line_material) # Set up camera self.camera_entity = self.view.camera() # camera_entity.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) self.camera_entity.setPosition(QVector3D(2, 2, 5)) self.camera_entity.setViewCenter(QVector3D(0, 0, 0)) # Set up orbit controller self.orbit_controller = Qt3DExtras.QOrbitCameraController(self.root) self.orbit_controller.setLinearSpeed(2.0) self.orbit_controller.setLookSpeed(100.0) self.orbit_controller.setCamera(self.camera_entity) # Add vertices after a delay QTimer.singleShot(2000, self.add_more_vertices) self.show() def add_more_vertices(self): new_vertices = [QVector3D(1.2, 1.2, 1.2), QVector3D(1.5, 1.5, 1.5), QVector3D(1.8, 1.8, 1.8)] if __name__ == "__main__": app = QApplication() window = MainWindow() app.exec()
解决方案
问题分析
- 索引数据格式错误:索引属性指定为
UnsignedInt类型,但原代码直接将整数列表转为bytearray,每个整数仅占1字节,而UnsignedInt需要4字节,导致索引解析错误 - 几何对象被GC回收:
line_geometry是局部变量,Python垃圾回收机制会在__init__执行后回收它,导致渲染时无法访问几何数据 - 动态更新逻辑缺失:
add_more_vertices方法仅定义了新顶点,未实现缓冲区更新、属性计数修改等逻辑
修复后的代码
from PySide6.QtCore import QTimer, QByteArray from PySide6.QtGui import QVector3D, QColor from PySide6.QtWidgets import QApplication, QMainWindow, QWidget from PySide6.Qt3DCore import Qt3DCore from PySide6.Qt3DExtras import Qt3DExtras from PySide6.Qt3DRender import Qt3DRender import struct class MainWindow(QMainWindow): def __init__(self): super().__init__() # 3D视图设置 self.view = Qt3DExtras.Qt3DWindow() self.view.defaultFrameGraph().setClearColor(QColor(255,255,255)) self.container = QWidget.createWindowContainer(self.view) self.setCentralWidget(self.container) # 3D场景设置 self.root = Qt3DCore.QEntity() self.view.setRootEntity(self.root) # 线条实体 self.line_entity = Qt3DCore.QEntity(self.root) # 将几何对象保存为成员变量,避免被GC回收 self.line_geometry = Qt3DCore.QGeometry(self.line_entity) # 初始化顶点数据 self.vertex_data = [QVector3D(0.0, 0.0, 0.0), QVector3D(1.0, 0.0, 0.0), QVector3D(1.0, 1.0, 0.0)] vertex_byteArray = QByteArray() for vector in self.vertex_data: vertex_byteArray.append(struct.pack('fff', vector.x(), vector.y(), vector.z())) # 顶点缓冲区 self.vertex_buffer = Qt3DCore.QBuffer(self.line_geometry) self.vertex_buffer.setData(vertex_byteArray) # 顶点属性 self.vertex_attribute = Qt3DCore.QAttribute() self.vertex_attribute.setName(Qt3DCore.QAttribute.defaultPositionAttributeName()) self.vertex_attribute.setAttributeType(Qt3DCore.QAttribute.AttributeType.VertexAttribute) self.vertex_attribute.setVertexBaseType(Qt3DCore.QAttribute.VertexBaseType.Float) self.vertex_attribute.setBuffer(self.vertex_buffer) self.vertex_attribute.setVertexSize(3) self.vertex_attribute.setByteOffset(0) self.vertex_attribute.setByteStride(3 * 4) self.vertex_attribute.setCount(len(self.vertex_data)) self.line_geometry.addAttribute(self.vertex_attribute) # 线条材质 self.line_material = Qt3DExtras.QPhongMaterial(self.root) self.line_material.setAmbient(QColor(255,0,0)) # 线条渲染器 self.line_renderer = Qt3DRender.QGeometryRenderer(self.root) self.line_renderer.setPrimitiveType(Qt3DRender.QGeometryRenderer.LineStrip) self.line_renderer.setGeometry(self.line_geometry) self.line_renderer.setVertexCount(len(self.vertex_data)) self.line_entity.addComponent(self.line_renderer) self.line_entity.addComponent(self.line_material) # 相机设置 self.camera_entity = self.view.camera() self.camera_entity.setPosition(QVector3D(2, 2, 5)) self.camera_entity.setViewCenter(QVector3D(0, 0, 0)) # 轨道控制器 self.orbit_controller = Qt3DExtras.QOrbitCameraController(self.root) self.orbit_controller.setLinearSpeed(2.0) self.orbit_controller.setLookSpeed(100.0) self.orbit_controller.setCamera(self.camera_entity) # 延迟添加顶点 QTimer.singleShot(2000, self.add_more_vertices) self.show() def add_more_vertices(self): # 添加新顶点 new_vertices = [QVector3D(1.2, 1.2, 1.2), QVector3D(1.5, 1.5, 1.5), QVector3D(1.8, 1.8, 1.8)] self.vertex_data.extend(new_vertices) # 更新顶点缓冲区数据 vertex_byteArray = QByteArray() for vector in self.vertex_data: vertex_byteArray.append(struct.pack('fff', vector.x(), vector.y(), vector.z())) self.vertex_buffer.setData(vertex_byteArray) # 更新顶点属性计数 self.vertex_attribute.setCount(len(self.vertex_data)) # 更新渲染器的顶点计数 self.line_renderer.setVertexCount(len(self.vertex_data)) # 通知几何对象数据已更改 self.line_geometry.markDirty(Qt3DCore.QGeometry.DirtyType.DirtyVertices) if __name__ == "__main__": app = QApplication() window = MainWindow() app.exec()
关键修复说明
- 保留几何对象引用:将
line_geometry、vertex_buffer、vertex_attribute都设为类成员变量,避免被Python垃圾回收 - 移除错误索引设置:使用
LineStrip类型时,不需要显式索引,直接通过顶点顺序绘制线条,解决索引格式错误问题 - 实现动态更新逻辑:在
add_more_vertices中更新顶点缓冲区数据、修改属性计数和渲染器顶点计数,并调用markDirty通知Qt3D几何数据已更改,触发重新渲染 - 解决 bounding volume 警告:正确设置顶点属性后,该警告会自动消失,因为Qt3D可以正确识别位置属性用于边界体积计算
内容的提问来源于stack exchange,提问作者Meliord
相关产品推荐
相关产品推荐

