2016-03-07 111 views
4

此問題可能與another SO question有關。PyQT5 OpenGL 4.1核心配置文件 - 無效的幀緩衝區操作 - Mac OS

我正在運行MacOS 10.11.2 El Capitan。希望豐富的GUI功能 圍繞我的OpenGL應用程序我決定給PyQT5一個創建OpenGL上下文的 ,這樣我就可以將我的OpenGL作爲一個QtWidget int整合到一個GUI應用程序中。

QT5提供QGlWidget幾個API方法,我很快在這裏總結一下:

  • ​​:以前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,) 
) 

我發現了一個website有關相關問題的報告。看起來,調用API方法時沒有配置幀緩衝區。因爲我覺得應該是QT的任務我沒有嘗試自己配置窗口幀緩衝區。但是我發現,在API方法的一些調用之後,幀緩衝是神奇地創建的。因此,我建立一個小黑客,等到paintGL被調用NSKIP_PAINTGL=3次。然後我配置我的對象,以便正常的paintGL進程開始工作。這似乎工作。但有時它需要超過NSKIP_PAINTGL次,所以我在解決方法中包含了一些sleep。 QT似乎應該在創建幀緩衝區之後。也許QT在一個單獨的線程中執行它?該QOpenGLWidget確認幀緩衝器可能不能在一段時間內被創建:

返回幀緩衝器對象句柄或0,如果尚未初始化。

我不喜歡這樣的工作,因爲我害怕這裏提出的條件。此外,我在這裏沒有很多控制權(我需要依靠QT首先調用paintGL的事實,以便黑客可以工作)。我不熟悉的那一刻QT框架,以便這裏是我的問題

如何創建,當創建QGLControllerWidget一些還挺循環,運行由一個try/catch覆蓋updateGL方法和長重試的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_() 

注意,同樣的C++代碼片段,歡迎我會嘗試轉換然後進入蟒蛇。

+0

呃......我不確定這個問題到底是什麼。你可以做一個C++測試用例嗎? – peppe

+0

我不熟悉C++。基本上問題是,在調用導致錯誤的paintGL()方法之前,QT不會初始化幀緩衝區。我將不勝感激一些提示如何實現一種控制機制,它允許我在第一次調用paintGL之前等待/強制幀緩衝區被初始化。啊哈! – keksnicoh

回答

2

QGL*東西在Qt5中已棄用。這可能是提前撥打paintGL()的原因。您應該嘗試使用QOpenGLWidgetQOpenGLWindow

或者,你可以嘗試使用內paintGL()isValid()方法和早期保釋出來,如果它返回一個falsy值:

def paintGL(self): 
    if not self.isValid(): 
     return 

如果你想嘗試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_()) 

此外,您不需要像我一樣使用Qt提供的OpenGL函數。你也可以使用OpenGL.GL的普通函數,或者你甚至可以混合搭配(Qt提供了一些很好的類,使得使用GL更容易)。如果您只是想使用OpenGL.GL,則可以刪除指代爲QOpenGLVersionProfile,versionprofile,versionFunctions,initializeOpenGLFunctionsself.gl的所有內容。

+0

啊!謝謝!沒有注意到QGl *已被棄用。必須將QSurfaceFormat的配置文件更改爲CoreProfile,並且這似乎在我的Mac上運行良好! – keksnicoh

+1

我在示例中將其從'CompatibilityProfile'更改爲'CoreProfile',以便匹配您的環境和您的問題(我沒有Mac)。 – blubberdiblub