PyQT5 OpenGL 4.1核心配置文件-无效的帧缓冲区操作-Mac OS

kek*_*coh 5 python opengl qt pyqt qt5

这个问题可能与另一个SO问题有关

我正在运行MacOS 10.11.2 El Capitan。希望在我的OpenGL应用程序周围具有丰富的GUI功能,我决定尝试PyQT5创建OpenGL上下文,以便将OpenGL作为QtWidget集成到GUI应用程序中。

QT5为QGlWidget提供了几种API方法,此我简要地总结一下:

  • initializeGL:之前被调用过一次 paintGL
  • paintGL:将内容绘制到活动帧缓冲区的地方

我能够创建小部件并初始化着色器等。但是当涉及到帧缓冲区相关操作时,会glClear出现错误:

File "errorchecker.pyx", line 53, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError (src/errorchecker.c:1218)

OpenGL.error.GLError: GLError(
    err = 1286,
    description = 'invalid framebuffer operation',
    baseOperation = glClear,
    cArguments = (GL_COLOR_BUFFER_BIT,)
)
Run Code Online (Sandbox Code Playgroud)

我发现了一个网站,报告相关问题。似乎在调用API方法时没有配置帧缓冲区。我觉得这应该是QT的任务,我没有尝试自己配置窗口帧缓冲区。但是我发现在调用API方法后,神奇地创建了帧缓冲区。因此,我建立了一个小技巧,它将等待paintGL被调用的NSKIP_PAINTGL=3时间。然后,配置我的对象,以便正常的paintGL进程开始工作。这似乎有效。但是有时需要的NSKIP_PAINTGL时间不止一次,因此我sleep在解决方法中加入了一些内容。QT似乎应该在应该创建之后创建帧缓冲区。也许QT是在单独的线程中进行的?该QOpenGLWidget 确认有时可能不会创建帧缓冲区:

返回值帧缓冲区对象句柄;如果尚未初始化,则返回0。

我不喜欢这样的工作环境,因为我担心这里的条件会提高。我在这里也没有太多的控制权(我需要依靠这样的事实,即QT首先经常调用paintGL,这样hack才能起作用)。我目前不熟悉QT框架,所以这是我的问题

如何创建某种循环,该循环在QGLControllerWidget创建时会运行updateGLa覆盖的方法try/catch并在GlError出现时重试?或者,循环可以侦听QOpenGLWidget::defaultFramebufferObject()并等待对象句柄。

当然,我想将这种技巧尽可能优雅地集成到QT应用程序流程中,这很可爱

还是我想念这里的东西?是否可以通过某种方式设置PyQT,以使其在有效的帧缓冲区存在之前不会调用OpenGL API方法?

这是在我的Mac上运行的带有黑客行为的隔离代码:

from PyQt5 import QtGui, QtCore, QtOpenGL, QtWidgets
from PyQt5.QtOpenGL import QGLWidget
from OpenGL.GL import *

from time import sleep

NSKIP_PAINTGL = 3

class QGLControllerWidget(QGLWidget):
    """
    basic test widget: a black screen which clears
    framebuffer on paintGL event so it should stay
    black on resize and so on.
    """
    def __init__(self, format = None):
        super(QGLControllerWidget, self).__init__(format, None)

        self._weird_pyqt5_framebuffer_hack = 0

        # replace paintGL by workaround
        self._weird_pyqt5_framebuffer_hack_original_paintGL = self.paintGL
        self.paintGL = self._weird_pyqt5_framebuffer_hack_paintGL

    def initializeGL(self): 
        pass

    def _weird_pyqt5_framebuffer_hack_paintGL(self):
        self._weird_pyqt5_framebuffer_hack += 1 
        if self._weird_pyqt5_framebuffer_hack < NSKIP_PAINTGL:
            return

        sleep(0.1)
        # restore original paintGL
        self.paintGL = self._weird_pyqt5_framebuffer_hack_original_paintGL
        self.updateGL()

    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT)

if __name__ == '__main__':
    import sys

    class QTWithGLTest(QtWidgets.QMainWindow):
        def __init__(self, parent = None):
            super(QTWithGLTest, self).__init__(parent)

            # MacOS core profile 4.1
            qgl_format = QtOpenGL.QGLFormat()
            qgl_format.setVersion(4, 1)
            qgl_format.setProfile(QtOpenGL.QGLFormat.CoreProfile)
            qgl_format.setSampleBuffers(True)

            self.widget = QGLControllerWidget(qgl_format)
            self.setCentralWidget(self.widget)

            self.show()

    app = QtWidgets.QApplication(sys.argv)
    window = QTWithGLTest()
    window.show()
    app.exec_()
Run Code Online (Sandbox Code Playgroud)

请注意,也欢迎使用C ++代码段,因为我将尝试将其转换为python。

blu*_*lub 4

这些QGL*东西在 Qt5 中已被弃用。paintGL()这可能就是它提前打电话的原因。您应该尝试使用QOpenGLWidgetQOpenGLWindow代替。

或者,您可以尝试使用isValid() 内部方法paintGL(),如果它返回错误值,则尽早退出:

def paintGL(self):
    if not self.isValid():
        return
Run Code Online (Sandbox Code Playgroud)

如果您想尝试QOpenGLWidget,可以以此为起点:

#!/usr/bin/env python

from PyQt5.QtGui import (
        QOpenGLBuffer,
        QOpenGLShader,
        QOpenGLShaderProgram,
        QOpenGLVersionProfile,
        QOpenGLVertexArrayObject,
        QSurfaceFormat,
    )
from PyQt5.QtWidgets import QApplication, QMainWindow, QOpenGLWidget


class QTWithGLTest(QMainWindow):
    """Main window."""

    def __init__(self, versionprofile=None, *args, **kwargs):
        """Initialize with an OpenGL Widget."""
        super(QTWithGLTest, self).__init__(*args, **kwargs)

        self.widget = QOpenGLControllerWidget(versionprofile=versionprofile)
        self.setCentralWidget(self.widget)
        self.show()


class QOpenGLControllerWidget(QOpenGLWidget):
    """Widget that sets up specific OpenGL version profile."""

    def __init__(self, versionprofile=None, *args, **kwargs):
        """Initialize OpenGL version profile."""
        super(QOpenGLControllerWidget, self).__init__(*args, **kwargs)

        self.versionprofile = versionprofile

    def initializeGL(self):
        """Apply OpenGL version profile and initialize OpenGL functions."""
        self.gl = self.context().versionFunctions(self.versionprofile)
        if not self.gl:
            raise RuntimeError("unable to apply OpenGL version profile")

        self.gl.initializeOpenGLFunctions()

        self.createShaders()
        self.createVBO()
        self.gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    def paintGL(self):
        """Painting callback that uses the initialized OpenGL functions."""
        if not self.gl:
            return

        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)
        self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, 3)

    def resizeGL(self, w, h):
        """Resize viewport to match widget dimensions."""
        self.gl.glViewport(0, 0, w, h)

    def createShaders(self):
        ...

    def createVBO(self):
        ...


if __name__ == '__main__':
    import sys

    fmt = QSurfaceFormat()
    fmt.setVersion(4, 1)
    fmt.setProfile(QSurfaceFormat.CoreProfile)
    fmt.setSamples(4)
    QSurfaceFormat.setDefaultFormat(fmt)

    vp = QOpenGLVersionProfile()
    vp.setVersion(4, 1)
    vp.setProfile(QSurfaceFormat.CoreProfile)

    app = QApplication(sys.argv)
    window = QTWithGLTest(versionprofile=vp)
    window.show()
    sys.exit(app.exec_())
Run Code Online (Sandbox Code Playgroud)

另外,你不需要像我一样使用Qt提供的OpenGL函数。您也可以使用其中的普通函数,OpenGL.GL甚至可以混合搭配(Qt 提供了一些很好的类,使使用 GL 更容易)。如果您只想使用OpenGL.GL,则可以删除所有涉及QOpenGLVersionProfile, versionprofile,versionFunctionsinitializeOpenGLFunctions的内容self.gl