Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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-512

    This 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


  • Lifetime Qt Champion

    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.so

    There 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


  • Lifetime Qt Champion

    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


  • Lifetime Qt Champion

    What technology are you using for your application ?



  • python + some bindings to external libraries. But the 3d aspects are fully written in pyqt. I can get rid of some funcitonalities if I can find a decent workaround



  • 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:
    with

    export 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 -1

    On 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-pyqt5

    And reported as a bug on qt jira:
    https://bugreports.qt.io/browse/QTBUG-91162

    With additional informations related to javascript logs.


Log in to reply