Search code examples
pythonopenglpyqt5portingpyqt6

A minimal OpenGL example in PyQt6 does not work. Error: "invalid operation" in glClearColor


I try to run a very simple OpenGL example:

import sys

from OpenGL import GL as gl
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
from PyQt6.QtWidgets import QApplication


class Widget(QOpenGLWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt6, OpenGL 3.3")
        self.resize(400, 400)

    def initializeGL(self):
        gl.glClearColor(0.5, 0.5, 0.5, 1)
    
    def paintGL(self):
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec())

But I have this error:

Traceback (most recent call last):
  File "main.py", line 16, in initializeGL
    gl.glClearColor(0, 0, 0, 1)
  File "E:\ProgramFiles\Python\Python38\lib\site-packages\OpenGL\platform\baseplatform.py", line 415, in __call__
    return self( *args, **named )
  File "E:\ProgramFiles\Python\Python38\lib\site-packages\OpenGL\error.py", line 230, in glCheckError
    raise self._errorClass(
OpenGL.error.GLError: GLError(
        err = 1282,
        description = b'invalid operation',
        baseOperation = glClearColor,
        cArguments = (0, 0, 0, 1)
)

It is the same example but in PyQt5 that works:

import sys

from OpenGL import GL as gl
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QOpenGLWidget


class Widget(QOpenGLWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt5, OpenGL 3.3")
        self.resize(400, 400)

    def initializeGL(self):
        gl.glClearColor(0.5, 0.5, 0.5, 1)
    
    def paintGL(self):
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

if __name__ == "__main__":
    QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

There are two differences:

    1. QOpenGLWidget was moved to PyQt6.QtOpenGLWidgets
    1. the line in the PyQt5 example: QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)

Solution

  • This line:

    QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
    

    should be replaced with this:

    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    

    These changes also apply to PySide6:

    1. OpenGL classes have been moved to a separate PyQt6.QtOpenGL namespace:

    PyQt5:

    from PyQt5.QtGui import (QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram,
                             QOpenGLTexture)
    

    PyQt6:

    from PyQt6.QtOpenGL import (QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram,
                                QOpenGLTexture)
    
    1. The QOpenGLWidget class has been moved to the PyQt6.QtOpenGLWidgets namespace:

    PyQt5:

    from PyQt5.QtWidgets import QApplication, QOpenGLWidget
    

    PyQt6:

    from PyQt6.QtOpenGLWidgets import QOpenGLWidget
    
    1. Changed enum for shader types:

    PyQt5:

    self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderSrc)
    self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderSrc)
    

    PyQt6:

    self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertShaderSrc)
    self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragShaderSrc)
    
    1. Changed enum Target texture:

    PyQt5:

    self.texture = QOpenGLTexture(QOpenGLTexture.Target2D)
    

    PyQt6:

    self.texture = QOpenGLTexture(QOpenGLTexture.Target.Target2D)
    
    1. Changed enum for setting texture filters:

    PyQt5:

    self.texture.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear)
    

    PyQt6:

    self.texture.setMinMagFilters(QOpenGLTexture.Filter.Linear, QOpenGLTexture.Filter.Linear)
    
    1. Changed enum for WrapMode:

    PyQt5:

    self.texture.setWrapMode(QOpenGLTexture.ClampToEdge)
    

    PyQt6:

    self.texture.setWrapMode(QOpenGLTexture.WrapMode.ClampToEdge)
    
    1. Changed enum to set application attributes:

    PyQt5:

    QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
    

    PyQt6:

    QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
    

    A few basic non-graphical changes:

    1. Changed enum for file open mode:

    PyQt5:

    file = QFile(path)
    if not file.open(QIODevice.ReadOnly):
        print("Failed to open the file: " + path)
    

    PyQt6:

    file = QFile(path)
    if not file.open(QIODevice.OpenModeFlag.ReadOnly):
        print("Failed to open the file: " + path)
    
    1. The QApplication.exec_() method has been renamed to QApplication.exec()

    PyQt5:

    import sys
    from PyQt5.QtWidgets import QApplication
    
    app = QApplication(sys.argv)
    sys.exit(app.exec_())
    

    PyQt6:

    import sys
    from PyQt6.QtWidgets import QApplication
    
    app = QApplication(sys.argv)
    sys.exit(app.exec())
    

    Falling Collada Cube with Bullet Physics, OpenGL 3.3:

    You should install these packages:

    • pip install PyQt6 (or PySide6)
    • pip install PyOpenGL
    • pip install numpy
    • pip install Panda3D (for Bullet Physics)

    enter image description here