Qt -platform webgl support in pyqt
-
Dear all,
I recently realized that QT had an amazing feature related to webgl support:
https://www.qt.io/blog/2018/11/23/qt-quick-webgl-release-512This feature is extremely important to me, to the point where I could eventually rewrite my application in C++ in case the feature is not supported in python. However that would save me a lot of time, if I could provide a -platform webgl option to my python app and get the webgl support.
My main object is a: PyQt5.Qt3DExtras.Qt3DWindow is there anywhere I could find information of the webgl support for pyqt ?
Thank you a lot for your help
-
Hi,
Did you check whether the corresponding QPA plugin is provided with your installation ?
-
@SGaist said in Qt -platform webgl support in pyqt:
QPA plugin
Thank you very much for your input, it helps me to check for the right concepts in the right place.
Here is what I have installer:venv/lib/python3.7/site-packages/PyQt5/Qt/plugins/platforms/
libqeglfs.so libqminimalegl.so libqoffscreen.so libqwayland-egl.so libqwayland-xcomposite-egl.so libqwebgl.so
libqlinuxfb.so libqminimal.so libqvnc.so libqwayland-generic.so libqwayland-xcomposite-glx.so libqxcb.soThere seems to be a libqwebgl.so I'll now check where I need to go from there with the documentation: https://doc.qt.io/qt-5/qpa.html
-
Then you can either start your application with the QT_QPA_PLATFORM environnement variable set to the correct value or pass the -platform option when starting your application.
-
@SGaist said in Qt -platform webgl support in pyqt:
QT_QPA_PLATFORM
Thank you so much for the help.
Unfortunately, it looks like my application is probably not going to be compatible with webgl:qt.qpa.webgl: WebGL QPA platform plugin: Raster surfaces are not supported Erreur de segmentation (core dumped)
@McTob said in Qt -platform webgl support in pyqt:
qt.qpa.webgl: WebGL QPA platform plugin: Raster surfaces are not supported
I can potentially remove some of the feature (getting webGL to work would really be a dream feature for me), from those, whoich are the one most likely generating the incompatibility:
from PyQt5.Qt3DExtras import QDiffuseSpecularMaterial, QTextureMaterial from PyQt5.Qt3DExtras import QText2DEntity, QExtrudedTextMesh from PyQt5.Qt3DRender import QTexture2D, QTextureLoader, QMaterial, QEffect
Is there a list of compatible feature somewhere ?
Thank you again
-
What technology are you using for your application ?
-
Ok so I tried to address the problem differently. I just tried to run the following demo code from https://github.com/zchen24/examples-Qt/blob/master/qt3d/qt3d-simple-example.py:
withexport QSG_INFO=1
export QT_QPA_EGLFS_DEBUG=1
export QT_LOGGING_RULES=qt.qpa.*=true
QT_QPA_PLATFORM=webgl:port=8998 python ./test.py#!/usr/bin/env python """ This is a Python port of Qt 3D: Simple C++ Example code https://doc.qt.io/qt-5.10/qt3d-simple-cpp-example.html Tested on Anaconda Python 3.6 pip install PyQt5 (Version 5.10) """ import sys from OpenGL import GL from PyQt5 import QtCore from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.Qt3DCore import * from PyQt5.QtWidgets import * from PyQt5.Qt3DExtras import * class OrbitTransformController(QObject): def __init__(self, parent): super(OrbitTransformController, self).__init__(parent) self.m_target = QTransform() self.m_matrix = QMatrix4x4() self.m_radius = 1.0 self.m_angle = 0 def getTarget(self): return self.m_target def setTarget(self, target): if self.m_target != target: self.m_target = target self.targetChanged.emit() def getRadius(self): return self.m_radius def setRadius(self, radius): if not QtCore.qFuzzyCompare(self.m_radius, radius): self.m_radius = radius self.updateMatrix() self.radiusChanged.emit() def getAngle(self): return self.m_angle def setAngle(self, angle): if not QtCore.qFuzzyCompare(angle, self.m_angle): self.m_angle = angle self.updateMatrix() self.angleChanged.emit() def updateMatrix(self): self.m_matrix.setToIdentity() self.m_matrix.rotate(self.m_angle, QVector3D(0, 1, 0)) self.m_matrix.translate(self.m_radius, 0, 0) self.m_target.setMatrix(self.m_matrix) # QSignal targetChanged = pyqtSignal() radiusChanged = pyqtSignal() angleChanged = pyqtSignal() # Qt properties target = pyqtProperty(QTransform, fget=getTarget, fset=setTarget) radius = pyqtProperty(float, fget=getRadius, fset=setRadius) angle = pyqtProperty(float, fget=getAngle, fset=setAngle) def createScene(): # root rootEntity = QEntity() material = QPhongMaterial(rootEntity) # torus torusEntity = QEntity(rootEntity) torusMesh = QTorusMesh() torusMesh.setRadius(5) torusMesh.setMinorRadius(1) torusMesh.setRings(100) torusMesh.setSlices(20) torusTransform = QTransform() torusTransform.setScale3D(QVector3D(1.5, 1.0, 0.5)) torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45)) torusEntity.addComponent(torusMesh) torusEntity.addComponent(torusTransform) torusEntity.addComponent(material) # sphere sphereEntity = QEntity(rootEntity) sphereMesh = QSphereMesh() sphereMesh.setRadius(3) sphereTransform = QTransform() controller = OrbitTransformController(sphereTransform) controller.setTarget(sphereTransform) controller.setRadius(20) sphereRotateTransformAnimation = QPropertyAnimation(sphereTransform) sphereRotateTransformAnimation.setTargetObject(controller) sphereRotateTransformAnimation.setPropertyName(b'angle') sphereRotateTransformAnimation.setStartValue(0) sphereRotateTransformAnimation.setEndValue(360) sphereRotateTransformAnimation.setDuration(10000) sphereRotateTransformAnimation.setLoopCount(-1) sphereRotateTransformAnimation.start() sphereEntity.addComponent(sphereMesh) sphereEntity.addComponent(sphereTransform) sphereEntity.addComponent(material) return rootEntity # container = QWidget.createWindowContainer(view) # container.show() app = QApplication(sys.argv) view = Qt3DWindow() scene = createScene() # camera camera = view.camera() camera.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000) camera.setPosition(QVector3D(0, 0, 40)) camera.setViewCenter(QVector3D(0, 0, 0)) # for camera control camController = QOrbitCameraController(scene) camController.setLinearSpeed( 50.0 ) camController.setLookSpeed( 180.0 ) camController.setCamera(camera) view.setRootEntity(scene) view.show() sys.exit(app.exec_())
Here are the logs on server side:
qt.qpa.webgl: WebGL QPA Plugin created
qt.qpa.input.methods.serialize: QIBusEngineDesc::fromDBusArgument() "(sa{sv}ssssssssussssssss)"
qt.qpa.input.methods: socketWatcher.addPath "/home/user/.config/ibus/bus/57acbc52de7a41f6bcba53d2d3273731-unix-1"
qt.qpa.webgl.httpserver: Listening in port 8998
qt.qpa.webgl: New offscreen surface 0x7ffcabece970
qt.qpa.webgl: 0x7ffcabece960
qt.qpa.webgl.context: Creating context 1
qt.qpa.webgl.context: 0x55d81a4eb2e0
qt.qpa.webgl: 0x7f02a40053b0
qt.qpa.webgl.context: Creating context 2
qt.qpa.webgl: 0x7f02a4005730
qt.qpa.webgl.context: Creating context 3
qt.qpa.webgl: Creating platform window for: 0x55d81a4d7100
qt.qpa.webgl: New offscreen surface 0x55d81a536160
qt.qpa.webgl.window: Destroying -1On client side:
WebGL warning: getParameter: pname: Invalid enum value <enum 0x8f38>In the end, I only see a green loading sign that cycles forever, and not the actual 3d webgl rendering I was expecting. Any idea ?
-
Ok so I tried to address the problem differently. I just tried to run the following demo code from https://github.com/zchen24/examples-Qt/blob/master/qt3d/qt3d-simple-example.py:
withexport QSG_INFO=1
export QT_QPA_EGLFS_DEBUG=1
export QT_LOGGING_RULES=qt.qpa.*=true
QT_QPA_PLATFORM=webgl:port=8998 python ./test.py#!/usr/bin/env python """ This is a Python port of Qt 3D: Simple C++ Example code https://doc.qt.io/qt-5.10/qt3d-simple-cpp-example.html Tested on Anaconda Python 3.6 pip install PyQt5 (Version 5.10) """ import sys from OpenGL import GL from PyQt5 import QtCore from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.Qt3DCore import * from PyQt5.QtWidgets import * from PyQt5.Qt3DExtras import * class OrbitTransformController(QObject): def __init__(self, parent): super(OrbitTransformController, self).__init__(parent) self.m_target = QTransform() self.m_matrix = QMatrix4x4() self.m_radius = 1.0 self.m_angle = 0 def getTarget(self): return self.m_target def setTarget(self, target): if self.m_target != target: self.m_target = target self.targetChanged.emit() def getRadius(self): return self.m_radius def setRadius(self, radius): if not QtCore.qFuzzyCompare(self.m_radius, radius): self.m_radius = radius self.updateMatrix() self.radiusChanged.emit() def getAngle(self): return self.m_angle def setAngle(self, angle): if not QtCore.qFuzzyCompare(angle, self.m_angle): self.m_angle = angle self.updateMatrix() self.angleChanged.emit() def updateMatrix(self): self.m_matrix.setToIdentity() self.m_matrix.rotate(self.m_angle, QVector3D(0, 1, 0)) self.m_matrix.translate(self.m_radius, 0, 0) self.m_target.setMatrix(self.m_matrix) # QSignal targetChanged = pyqtSignal() radiusChanged = pyqtSignal() angleChanged = pyqtSignal() # Qt properties target = pyqtProperty(QTransform, fget=getTarget, fset=setTarget) radius = pyqtProperty(float, fget=getRadius, fset=setRadius) angle = pyqtProperty(float, fget=getAngle, fset=setAngle) def createScene(): # root rootEntity = QEntity() material = QPhongMaterial(rootEntity) # torus torusEntity = QEntity(rootEntity) torusMesh = QTorusMesh() torusMesh.setRadius(5) torusMesh.setMinorRadius(1) torusMesh.setRings(100) torusMesh.setSlices(20) torusTransform = QTransform() torusTransform.setScale3D(QVector3D(1.5, 1.0, 0.5)) torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45)) torusEntity.addComponent(torusMesh) torusEntity.addComponent(torusTransform) torusEntity.addComponent(material) # sphere sphereEntity = QEntity(rootEntity) sphereMesh = QSphereMesh() sphereMesh.setRadius(3) sphereTransform = QTransform() controller = OrbitTransformController(sphereTransform) controller.setTarget(sphereTransform) controller.setRadius(20) sphereRotateTransformAnimation = QPropertyAnimation(sphereTransform) sphereRotateTransformAnimation.setTargetObject(controller) sphereRotateTransformAnimation.setPropertyName(b'angle') sphereRotateTransformAnimation.setStartValue(0) sphereRotateTransformAnimation.setEndValue(360) sphereRotateTransformAnimation.setDuration(10000) sphereRotateTransformAnimation.setLoopCount(-1) sphereRotateTransformAnimation.start() sphereEntity.addComponent(sphereMesh) sphereEntity.addComponent(sphereTransform) sphereEntity.addComponent(material) return rootEntity # container = QWidget.createWindowContainer(view) # container.show() app = QApplication(sys.argv) view = Qt3DWindow() scene = createScene() # camera camera = view.camera() camera.lens().setPerspectiveProjection(45.0, 16.0/9.0, 0.1, 1000) camera.setPosition(QVector3D(0, 0, 40)) camera.setViewCenter(QVector3D(0, 0, 0)) # for camera control camController = QOrbitCameraController(scene) camController.setLinearSpeed( 50.0 ) camController.setLookSpeed( 180.0 ) camController.setCamera(camera) view.setRootEntity(scene) view.show() sys.exit(app.exec_())
Here are the logs on server side:
qt.qpa.webgl: WebGL QPA Plugin created
qt.qpa.input.methods.serialize: QIBusEngineDesc::fromDBusArgument() "(sa{sv}ssssssssussssssss)"
qt.qpa.input.methods: socketWatcher.addPath "/home/user/.config/ibus/bus/57acbc52de7a41f6bcba53d2d3273731-unix-1"
qt.qpa.webgl.httpserver: Listening in port 8998
qt.qpa.webgl: New offscreen surface 0x7ffcabece970
qt.qpa.webgl: 0x7ffcabece960
qt.qpa.webgl.context: Creating context 1
qt.qpa.webgl.context: 0x55d81a4eb2e0
qt.qpa.webgl: 0x7f02a40053b0
qt.qpa.webgl.context: Creating context 2
qt.qpa.webgl: 0x7f02a4005730
qt.qpa.webgl.context: Creating context 3
qt.qpa.webgl: Creating platform window for: 0x55d81a4d7100
qt.qpa.webgl: New offscreen surface 0x55d81a536160
qt.qpa.webgl.window: Destroying -1On client side:
WebGL warning: getParameter: pname: Invalid enum value <enum 0x8f38>In the end, I only see a green loading sign that cycles forever, and not the actual 3d webgl rendering I was expecting. Any idea ?
I also tried from my operating system (ubuntun 20.04) python3 and (sudo apt-get install python3-opengl), I got slightly different results (but the webpage is still hanging on the loading sign):
qt.qpa.webgl: WebGL QPA Plugin created
qt.qpa.input.methods.serialize: QIBusEngineDesc::fromDBusArgument() "(sa{sv}ssssssssussssssss)"
qt.qpa.input.methods: socketWatcher.addPath "/home/user/.config/ibus/bus/57acbc52de7a41f6bcba53d2d3273731-unix-1"
qt.qpa.webgl.httpserver: Listening in port 8998
qt.qpa.webgl: New offscreen surface 0x7ffdf3b88660
qt.qpa.webgl: 0x7ffdf3b88650
qt.qpa.webgl.context: Creating context 1
qt.qpa.webgl.context: 0x1784000
qt.qpa.webgl: 0x7f21840053b0
qt.qpa.webgl.context: Creating context 2
qt.qpa.webgl: 0x7f2184005730
qt.qpa.webgl.context: Creating context 3
qt.qpa.webgl: Creating platform window for: 0x17705f0
qt.qpa.webgl: New offscreen surface 0x17d8680
qt.qpa.webgl.window: Destroying -1
qt.qpa.webgl.httpserver: ::1 requested: /
qt.qpa.webgl.httpserver: ::1 requested: /webqt.js
qt.qpa.webgl.websocketserver: Sending connect to QWebSocket(0x7f218800c270) QMap(("debug", QVariant(bool, false))("loadingScreen", QVariant(QByteArray, ""))("mouseTracking", QVariant(QByteArray, ""))("supportedFunctions", QVariant(QStringList, ("activeTexture", "attachShader", "bindAttribLocation", "bindBuffer", "bindFramebuffer", "bindRenderbuffer", "bindTexture", "blendColor", "blendEquation", "blendEquationSeparate", "blendFunc", "blendFuncSeparate", "bufferData", "bufferSubData", "checkFramebufferStatus", "clear", "clearColor", "clearDepthf", "clearStencil", "colorMask", "compileShader", "compressedTexImage2D", "compressedTexSubImage2D", "copyTexImage2D", "copyTexSubImage2D", "createProgram", "createShader", "cullFace", "deleteBuffers", "deleteFramebuffers", "deleteProgram", "deleteRenderbuffers", "deleteShader", "deleteTextures", "depthFunc", "depthMask", "depthRangef", "detachShader", "disableVertexAttribArray", "drawArrays", "drawElements", "enableVertexAttribArray", "finish", "flush", "framebufferRenderbuffer", "framebufferTexture2D", "frontFace", "genBuffers", "genFramebuffers", "genRenderbuffers", "genTextures", "generateMipmap", "getActiveAttrib", "getActiveUniform", "getAttachedShaders", "getAttribLocation", "getString", "getIntegerv", "getBooleanv", "enable", "disable", "getBufferParameteriv", "getError", "getParameter", "getFramebufferAttachmentParameteriv", "getProgramInfoLog", "getProgramiv", "getRenderbufferParameteriv", "getShaderInfoLog", "getShaderPrecisionFormat", "getShaderSource", "getShaderiv", "getTexParameterfv", "getTexParameteriv", "getUniformLocation", "getUniformfv", "getUniformiv", "getVertexAttribPointerv", "getVertexAttribfv", "getVertexAttribiv", "hint", "isBuffer", "isEnabled", "isFramebuffer", "isProgram", "isRenderbuffer", "isShader", "isTexture", "lineWidth", "linkProgram", "pixelStorei", "polygonOffset", "readPixels", "releaseShaderCompiler", "renderbufferStorage", "sampleCoverage", "scissor", "shaderBinary", "shaderSource", "stencilFunc", "stencilFuncSeparate", "stencilMask", "stencilMaskSeparate", "stencilOp", "stencilOpSeparate", "texImage2D", "texParameterf", "texParameterfv", "texParameteri", "texParameteriv", "texSubImage2D", "uniform1f", "uniform1fv", "uniform1i", "uniform1iv", "uniform2f", "uniform2fv", "uniform2i", "uniform2iv", "uniform3f", "uniform3fv", "uniform3i", "uniform3iv", "uniform4f", "uniform4fv", "uniform4i", "uniform4iv", "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv", "useProgram", "validateProgram", "vertexAttrib1f", "vertexAttrib1fv", "vertexAttrib2f", "vertexAttrib2fv", "vertexAttrib3f", "vertexAttrib3fv", "vertexAttrib4f", "vertexAttrib4fv", "vertexAttribPointer", "viewport", "blitFramebufferEXT", "renderbufferStorageMultisampleEXT", "getTexLevelParameteriv", "makeCurrent", "swapBuffers")))("sysinfo", QVariant(QVariantMap, QMap(("buildAbi", QVariant(QString, "x86_64-little_endian-lp64"))("buildCpuArchitecture", QVariant(QString, "x86_64"))("currentCpuArchitecture", QVariant(QString, "x86_64"))("kernelType", QVariant(QString, "linux"))("machineHostName", QVariant(QString, "supercomputer"))("prettyProductName", QVariant(QString, "Ubuntu 20.04.2 LTS"))("productType", QVariant(QString, "ubuntu"))("productVersion", QVariant(QString, "20.04"))))))
qt.qpa.webgl: 0x7f218800c270, Size: 1848x912. Physical Size: 490.755838x242.191193
qt.qpa.webgl.httpserver: ::1 requested: /favicon.png
qt.qpa.webgl: Connecting first client in the queue (0x7f218800c270)
qt.qpa.webgl: Creating platform window for: 0x17705f0
qt.qpa.webgl.window: Window 1 created
qt.qpa.webgl: Created platform window 0x18192d0 for: 0x17705f0
qt.qpa.webgl.websocketserver: Sending create_canvas to QWebSocket(0x7f218800c270) QMap(("height", QVariant(int, 912))("title", QVariant(QString, "test.py"))("width", QVariant(int, 1848))("winId", QVariant(qulonglong, 1))("x", QVariant(int, 0))("y", QVariant(int, 0)))
qt.qpa.webgl.context: 0x18192d0
qt.qpa.webgl.websocketserver: Sending gl_command makeCurrent to 0x7f218800c270 with 4 parameters
qt.qpa.webgl.websocketserver: Sending gl_command glGetIntegerv to 0x7f218800c270 with 1 parameters
qt.qpa.webgl: gl_response message received QJsonObject({"id":1,"type":"gl_response","value":32})
qt.qpa.webgl.websocketserver: Sending gl_command glGetIntegerv to 0x7f218800c270 with 1 parameters
qt.qpa.webgl: gl_response message received QJsonObject({"id":2,"type":"gl_response","value":null}) -
Looks like this problem goes deeper than a simple pyqt issue. I have posted my issue on SO:
https://stackoverflow.com/questions/66222702/how-to-get-webgl-support-in-pyqt5And reported as a bug on qt jira:
https://bugreports.qt.io/browse/QTBUG-91162With additional informations related to javascript logs.