pyvista-support
pyvista-support copied to clipboard
Help Needed: Can not get Pyvista render to work with Qt QML QQuickFramebufferObject
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
- https://stackoverflow.com/questions/61950058/qquickframebufferobject-causes-crash-in-pyside2
- https://github.com/nicanor-romero/QtVtk/blob/master/src/QVTKFramebufferObjectRenderer.cpp
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?
any update ?
I'm looking forward to having QML support in PyVista. Meanwhile, I found other vtk-qml examples: