Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help Needed: Can not get Pyvista render to work with Qt QML QQuickFramebufferObject #473

Open
bakkiaraj opened this issue Jul 25, 2021 · 2 comments

Comments

@bakkiaraj
Copy link

bakkiaraj commented Jul 25, 2021

I have multiple Pyvista 3d visualizations render in the Qt 5.15.x Qt Widgets. This was done using QtInteractor (https://github.com/pyvista/pyvistaqt). Now, I am moving away from Qt widgets to Qt QML.

My idea is to get the renderer from PyVista and get it render in QML QQuickFrameBufferObject. Based on knowledge from

I wrote a following Python3 code.
File: 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_())

File: 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!")
        }
}

Since PyVista's renderer already have a Sphere 3d object, My expectation is that, It gets render in the QML's framebuffer object and will be visible in the QML window. But, I am wrong. There is no crash but I also do not see Sphere 3d object in the QML window.

QML QSG_VISUALIZE debugging shows there is a FBO object is created but could not get PyVista's renderer to work with QML framebuffer.

Output:
qml_fbo_pyvista.py UI output
qml_fbo_pyvista.py consoleoutput

Environment:

                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

What am I missing?

@salim97
Copy link

salim97 commented Oct 25, 2021

any update ?

@liushuya7
Copy link

liushuya7 commented Dec 30, 2021

I'm looking forward to having QML support in PyVista. Meanwhile, I found other vtk-qml examples:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants