3

我在 Qt 5.15.x Qt Widgets 中有多个 Pyvista 3d 可视化渲染。这是使用 QtInteractor ( https://github.com/pyvista/pyvistaqt ) 完成的。现在,我正在从 Qt 小部件转向 Qt QML。

我的想法是从 PyVista 获取渲染器并在 QML QQuickFrameBufferObject 中渲染。根据来自的知识

我编写了以下 Python3 代码。文件:qml_fbo_pyvista.py

from __future__ import annotations

import os
import sys

import pyvista as pv
from PySide2.QtCore import QSize
from PySide2.QtGui import QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLFunctions
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide2.QtQuick import QQuickWindow, QQuickFramebufferObject
from PySide2.QtWidgets import QApplication
# https://doc.qt.io/qt-5/qquickframebufferobject-renderer.html
from pyvista import PolyData, BasePlotter
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkRenderingCore import vtkRenderWindowInteractor, vtkRenderer
from vtkmodules.vtkRenderingExternal import vtkExternalOpenGLRenderWindow
from vtkmodules.vtkRenderingOpenGL2 import vtkOpenGLActor
from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor


class PyvistaFBORenderer(QQuickFramebufferObject.Renderer, BasePlotter):
    _fbo: QOpenGLFramebufferObject
    _m_fboItem: QQuickFramebufferObject

    def __init__(self) -> None:
        super().__init__()
        print("* init renderer")

        # surfaceFormat: QSurfaceFormat = QSurfaceFormat()
        # surfaceFormat.setRenderableType(QSurfaceFormat.OpenGL)
        # # surfaceFormat.setVersion(3, 2)
        # # surfaceFormat.setProfile(QSurfaceFormat.CoreProfile)
        # # surfaceFormat.setSwapBehavior(QSurfaceFormat.DoubleBuffer)
        # # surfaceFormat.setRedBufferSize(8)
        # # surfaceFormat.setGreenBufferSize(8)
        # # surfaceFormat.setBlueBufferSize(8)
        # # surfaceFormat.setDepthBufferSize(24)
        # # surfaceFormat.setStencilBufferSize(8)
        # # surfaceFormat.setAlphaBufferSize(0)
        # # surfaceFormat.setStereo(False)
        # QSurfaceFormat.setDefaultFormat(surfaceFormat)

        self.plotter = pv.Plotter(off_screen=True,
                                  polygon_smoothing=True,
                                  line_smoothing=True,
                                  point_smoothing=True)

        self.geom: PolyData = pv.Sphere()
        self.m_actor: vtkOpenGLActor = self.plotter.add_mesh(self.geom)
        self.m_renderer: vtkRenderer = self.plotter.renderer

        self.m_interactor: vtkRenderWindowInteractor = vtkGenericRenderWindowInteractor()
        self.m_renderWindow: vtkExternalOpenGLRenderWindow = vtkExternalOpenGLRenderWindow()

        self.m_renderWindow.SetInteractor(self.m_interactor)

        self.m_renderer.SetBackground(0,0,0)
        self.m_renderWindow.AddRenderer(self.m_renderer)

        interactor_style = vtkInteractorStyleTrackballCamera()
        self.m_interactor.SetInteractorStyle(interactor_style)

        self.m_interactor.Initialize()

        self.update()


    def createFramebufferObject(self, size: QSize) -> QOpenGLFramebufferObject:
        print("* createFramebufferObject called")
        fmt = QOpenGLFramebufferObjectFormat()
        fmt.setAttachment(QOpenGLFramebufferObject.Depth)
        self._fbo = QOpenGLFramebufferObject(size, fmt)

        self.m_renderWindow.SetBackLeftBuffer(0x8CE0)
        self.m_renderWindow.SetFrontLeftBuffer(0x8CE0)

        self.m_renderWindow.SetSize(size.width(), size.height())
        self.m_renderWindow.SetOffScreenRendering(True)
        self.m_renderWindow.Modified()

        return self._fbo

    def synchronize(self, item: QQuickFramebufferObject) -> None:
        print("* Sync")
        self._m_fboItem = item
        globj = QOpenGLFunctions()
        globj.initializeOpenGLFunctions()

        size = (item.size() * item.window().devicePixelRatio()).toSize()
        self.m_renderWindow.SetSize(size.width(), size.height())

    def render(self) -> None:
        print(" * Rendering")
        self.m_renderWindow.PushState()
        self.m_renderWindow.OpenGLInitState()
        self.m_renderWindow.MakeCurrent()

        globj = QOpenGLFunctions()
        globj.initializeOpenGLFunctions()
        globj.glEnable(0x809D)
        globj.glUseProgram(0)

        self.m_renderWindow.Start()

        self.m_renderWindow.Render()
        self.m_renderWindow.PopState()
        self._m_fboItem.window().resetOpenGLState()



# https://doc.qt.io/qt-5/qquickframebufferobject.html
class QMLPyvistaOpenGLItem(QQuickFramebufferObject):
    __renderer_obj: PyvistaFBORenderer

    def __init__(self) -> None:
        super().__init__()
        print("* created new PyvistaFBORenderer")

    def createRenderer(self) -> QQuickFramebufferObject.Renderer:
        self.__renderer_obj = PyvistaFBORenderer()
        print("* return PyvistaFBORenderer")
        return self.__renderer_obj


if __name__ == '__main__':

    os.environ['QSG_VISUALIZE'] = 'overdraw'


    app = QApplication(sys.argv)

    qmlRegisterType(QMLPyvistaOpenGLItem, "QMLPyvistaOpenGLItem", 1, 0, "QMLPyvistaOpenGLItem")

    engine = QQmlApplicationEngine()

    engine.load("./qml_view/pyvista3d_view.qml")
    if not engine.rootObjects():
        print("ERROR loading pyvista3d_view.qml")
        sys.exit(-1)

    mainWin: QQuickWindow = engine.rootObjects()[0]
    mainWin.show()
    sys.exit(app.exec_())

文件:qml_view/pyvista3d_view.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
import QMLPyvistaOpenGLItem 1.0

ApplicationWindow {
    id: mainapp
    width: 800
    height: 600
    title: "Hello Pyvista!!"
    visible: true
    color: "#2e03eb"
    Material.theme: Material.Dark
    Material.accent: Material.Indigo

    Button{
        text: "Hello"
        width: 100
        height: 100
    }

    QMLPyvistaOpenGLItem{
            anchors.fill: parent
            visible: true
            Component.onCompleted: console.log(" *********** Completed QMLPyvistaOpenGLItem!")
        }
}

由于 PyVista 的渲染器已经有一个 Sphere 3d 对象,我的期望是,它会在 QML 的帧缓冲区对象中渲染,并将在 QML 窗口中可见。但是,我错了。没有崩溃,但我也没有在 QML 窗口中看到 Sphere 3d 对象。

QML QSG_VISUALIZE 调试显示创建了一个 FBO 对象,但无法让 PyVista 的渲染器与 QML 帧缓冲区一起工作。

输出: qml_fbo_pyvista.py UI 输出 qml_fbo_pyvista.py 控制台输出

环境:

                OS : Windows
            CPU(s) : 12
           Machine : AMD64
      Architecture : 64bit
       Environment : Python
        GPU Vendor : Intel
      GPU Renderer : Intel(R) UHD Graphics
       GPU Version : 4.5.0 - Build 27.20.100.8681

  Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit
  (AMD64)]

           pyvista : 0.31.1
               vtk : 9.0.20210612
             numpy : 1.21.0
           imageio : 2.9.0
           appdirs : 1.4.4
            scooby : 0.5.7
            meshio : 4.4.6
        matplotlib : 3.4.2
         pyvistaqt : 0.5.0
           IPython : 7.25.0
             scipy : 1.7.0
           Pyvista2 : 5.15.4

            Qt:  5.15.4

我错过了什么?

4

0 回答 0