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

PySide6中Qt3D绘制3D线条失败及动态添加点问题求助

问题
  • 在Qt6/PySide6的Qt3D模块中绘制基础3D线条失败,参考旧Qt5 C++代码改写后窗口为空,线条未渲染
  • 核心需求:展示3D空间中运动物体的路径,支持初始化后从IMU持续数据流动态添加路径点
  • 运行代码时出现两个警告:
    1. Qt3D.Renderer.RHI.Backend: Initializing RHI with DirectX backend
    2. findBoundingVolumeComputeData: 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()
解决方案

问题分析

  1. 索引数据格式错误:索引属性指定为UnsignedInt类型,但原代码直接将整数列表转为bytearray,每个整数仅占1字节,而UnsignedInt需要4字节,导致索引解析错误
  2. 几何对象被GC回收:line_geometry是局部变量,Python垃圾回收机制会在__init__执行后回收它,导致渲染时无法访问几何数据
  3. 动态更新逻辑缺失: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()

关键修复说明

  1. 保留几何对象引用:将line_geometry、vertex_buffer、vertex_attribute都设为类成员变量,避免被Python垃圾回收
  2. 移除错误索引设置:使用LineStrip类型时,不需要显式索引,直接通过顶点顺序绘制线条,解决索引格式错误问题
  3. 实现动态更新逻辑:在add_more_vertices中更新顶点缓冲区数据、修改属性计数和渲染器顶点计数,并调用markDirty通知Qt3D几何数据已更改,触发重新渲染
  4. 解决 bounding volume 警告:正确设置顶点属性后,该警告会自动消失,因为Qt3D可以正确识别位置属性用于边界体积计算

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:00:55