这个问题可能与another SO question有关。
我正在运行MacOS 10.11.2 El Capitan。希望丰富的GUI功能 围绕我的OpenGL应用程序,我决定给PyQT5一个机会 创建OpenGL上下文,这样我就可以将我的OpenGL集成为GUI应用程序中的QtWidget int。
QT5为QGlWidget提供了几种API方法,我在这里简要总结一下:
initializeGL
:在paintGL
paintGL
:将内容绘制到活动帧缓冲区我可以创建窗口小部件并初始化着色器等。但是当遇到像glClear
这样的 framebuffer 相关操作时,会出现错误:
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方法后,帧缓冲区是神奇地创建的。因此,我构建了一个小的hack,它会等到paintGL
被调用NSKIP_PAINTGL=3
次。然后我配置我的对象,以便正常的paintGL过程开始工作。这似乎有效。但有时它需要超过NSKIP_PAINTGL
次,因此我在变通方法中包含了一点sleep
。 QT似乎在它应该之后创建了一个帧缓冲区。也许QT在一个单独的线程中做到了? QOpenGLWidget确认可能无法在某个时间创建帧缓冲区:
返回帧缓冲区对象句柄,如果尚未初始化,则返回0。
我不喜欢像这样的工作,因为我害怕这里有条件。此外,我没有很多控制权(我需要依赖于QT首先调用paintGL的事实,因此黑客可以工作)。我目前不熟悉QT框架所以这是我的问题:
如何创建某种类型的循环,在创建QGLControllerWidget
时,运行updateGL
覆盖的try/catch
方法,并在GlError
出现时重试?或者,循环可以监听QOpenGLWidget::defaultFramebufferObject()
并等待对象句柄。
当然,我希望将这个hack尽可能优雅地集成到QT应用程序流程中 - 以可爱的方式实现。
或者我在这里错过了什么?是否可以以某种方式设置PyQT,以便在有效的帧缓冲存在之前不会调用OpenGL API方法?
这是一个孤立的代码,其中包含在我的Mac上运行的hack:
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 ++片段也是受欢迎的,因为我将尝试将其转换为python。
答案 0 :(得分:3)
Qt5中不推荐使用QGL*
内容。这可能是它提前致电paintGL()
的原因。您应该尝试使用QOpenGLWidget或QOpenGLWindow代替。
或者,您可以尝试在isValid()
内使用paintGL()
方法,如果它返回虚假值,则提前纾困:
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
,initializeOpenGLFunctions
和self.gl
的所有内容。