如何在QWidget基类QMainWindow中集成Qt3D视图?
在QMainWindow中集成Qt3D视图的可行方案
我之前也踩过类似的坑,咱们一步步拆解问题,最后给你一个稳定可行的集成方案。
方案1的问题分析与修复
你遇到的RuntimeError: wrapped C/C++ object of type Qt3DWindow has been deleted,核心原因是Qt3DWindow对象被Python垃圾回收机制提前销毁;后续的OpenGL上下文错误,则是因为容器窗口没有被正确"暴露"(non-exposed),导致swapBuffers无法正常工作。
正确实现代码
要解决这些问题,需要确保三个关键点:
Qt3DWindow作为类成员变量存在,避免被垃圾回收- 容器部件正确绑定父对象并添加到布局
- 确保窗口初始化后处于可见状态
这里是修复后的完整可运行代码,包含QMainWindow的集成逻辑:
from PySide6.QtWidgets import QMainWindow, QWidget, QHBoxLayout, QApplication from PySide6.Qt3DExtras import (Qt3DWindow, QFirstPersonCameraController, QPhongMaterial, QCuboidMesh) from PySide6.Qt3DCore import QEntity, QTransform from PySide6.QtGui import QVector3D def createScene(): # 创建场景根实体 rootEntity = QEntity() # 添加一个立方体作为示例 cubeEntity = QEntity(rootEntity) cubeMesh = QCuboidMesh() cubeMaterial = QPhongMaterial() cubeTransform = QTransform() cubeTransform.setScale(4.0) cubeEntity.addComponent(cubeMesh) cubeEntity.addComponent(cubeMaterial) cubeEntity.addComponent(cubeTransform) return rootEntity class View3D(QWidget): def __init__(self, parent=None): super().__init__(parent) # 初始化Qt3DWindow并作为类成员保存 self.view = Qt3DWindow() # 创建容器并绑定父对象,避免被意外销毁 self.container = QWidget.createWindowContainer(self.view, self) # 设置布局,确保容器占满整个部件 layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.container) self.setLayout(layout) # 加载场景并配置相机 scene = createScene() camera = self.view.camera() camera.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000.0) camera.setPosition(QVector3D(0, 0, 40.0)) camera.setViewCenter(QVector3D(0, 0, 0)) # 添加第一人称相机控制器 camController = QFirstPersonCameraController(scene) camController.setLinearSpeed(50.0) camController.setLookSpeed(180.0) camController.setCamera(camera) # 绑定场景到Qt3DWindow self.view.setRootEntity(scene) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Qt3D in QMainWindow") self.setGeometry(100, 100, 800, 600) # 添加菜单、状态栏(示例) self.menuBar().addMenu("File") self.statusBar().showMessage("Ready") # 将3D视图设置为主窗口中心部件 self.view3d = View3D() self.setCentralWidget(self.view3d) if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() app.exec()
关键修复点
- 用
QWidget.createWindowContainer时指定父部件为self,确保容器的生命周期和View3D绑定 self.view作为类成员存在,避免被Python的垃圾回收机制提前销毁- 布局设置
setContentsMargins(0,0,0,0),确保3D视图占满容器空间,解决窗口暴露问题
方案2的局限性
QGLWidget是Qt中较旧的底层OpenGL部件,它直接操作OpenGL上下文;而Qt3D是基于场景图架构的高级3D框架,两者的渲染机制完全不兼容。因此用QGLWidget确实无法调用Qt3DWindow的场景图接口,这个方案本身就不适合你的需求,建议直接放弃。
额外注意事项
- 如果遇到OpenGL版本兼容问题,可以在QApplication初始化前设置默认格式:
from PySide6.QtGui import QSurfaceFormat format = QSurfaceFormat() format.setVersion(4, 1) format.setProfile(QSurfaceFormat.CoreProfile) QSurfaceFormat.setDefaultFormat(format)
- 主窗口的菜单、状态栏可以正常保留,不会和3D视图产生冲突
内容的提问来源于stack exchange,提问作者Overdrivr
相关产品推荐
相关产品推荐

