如何在Python GTK中为GtkGLArea设置OpenGL版本
问题描述
我编写了一个基于GtkGLArea的类,在Windows系统中使用OpenGL 3.0可正常运行,但在Linux系统中运行失败。报错显示在OpenGL 4.6上下文中调用了OpenGL 2.0相关方法,具体错误堆栈如下:
Traceback (most recent call last): File "/home/user/git/DisplayIn/src/displayin/engine/openglrenderer.py", line 190, in render self.setupGraphics() File "/home/user/git/DisplayIn/src/displayin/engine/openglrenderer.py", line 120, in setupGraphics self.initBuffers() File "/home/user/git/DisplayIn/src/displayin/engine/openglrenderer.py", line 133, in initBuffers glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0)) File "/usr/local/lib/python3.10/dist-packages/OpenGL/latebind.py", line 63, in __call__ return self.wrapperFunction( self.baseFunction, *args, **named ) File "/usr/local/lib/python3.10/dist-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer contextdata.setValue( key, array ) File "/usr/local/lib/python3.10/dist-packages/OpenGL/contextdata.py", line 58, in setValue context = getContext( context ) File "/usr/local/lib/python3.10/dist-packages/OpenGL/contextdata.py", line 40, in getContext raise error.Error( OpenGL.error.Error: Attempt to retrieve context when no valid context
相关实现代码如下:
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from OpenGL.GL import * import numpy as np import gc VERTEX_SOURCE = ''' #version 330 layout (location=0) in vec3 position; layout (location=1) in vec3 color; layout (location=2) in vec2 texCoord; out vec3 ourColor; out vec2 TexCoord; void main() { gl_Position = vec4(position,1.0); ourColor = color; TexCoord= vec2(texCoord.x,1.0-texCoord.y); }''' FRAGMENT_SOURCE =''' #version 330 in vec3 ourColor; in vec2 TexCoord; out vec4 color; uniform sampler2D ourTexture; void main(){ color = texture(ourTexture, TexCoord); };''' recVertices = np.array([ # Positions Colors Texture Coords 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, # Top Right 0 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, # Bottom Right 1 -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, # Bottom Left 2 -1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, # Top Left 3 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, # Top Right 4 ], dtype=np.float32) def checkGlError(op: str): error = glGetError() if error is not None and error != 0: print("after %s() glError (0x%x)", op, error) class OpenGLRenderer(Gtk.GLArea): def __init__(self): Gtk.GLArea.__init__(self) self.connect("realize", self.onRealize) self.connect("render", self.onRender) self.ctx = None self.frame = self.createBlankScreenFrame() self.area = None self.shaderProgram = None self.positionHandle = None self.textureId = None self.vao = None self.vbos = None self.version = None def getVersion(self): major = glGetIntegerv(GL_MAJOR_VERSION) minor = glGetIntegerv(GL_MINOR_VERSION) version = glGetString(GL_VERSION) return major, minor, version def createBlankScreenFrame(self): frame = np.array([[[0, 0, 0]]], dtype=np.uint8) return frame def onRealize(self, area): error = area.get_error() if error != None: print("your graphics card is probably too old : ", error) else: print(area, "realize... fine so far") self.ctx = self.get_context() self.ctx.make_current() major, minor, self.version = self.getVersion() print("OpenGL realized", self.ctx) print("%s\n", self.version) def onRender(self, area, ctx): # Render will be invoked separately # self.render(self.frame) return True def setupGraphics(self): if self.shaderProgram is None: vertexShader = glCreateShader(GL_VERTEX_SHADER) glShaderSource(vertexShader, VERTEX_SOURCE) glCompileShader(vertexShader) status = glGetShaderiv(vertexShader, GL_COMPILE_STATUS) print("Compile vertexShader status: " + str(status == GL_TRUE)) pixelShader = glCreateShader(GL_FRAGMENT_SHADER) glShaderSource(pixelShader, FRAGMENT_SOURCE) glCompileShader(pixelShader) status = glGetShaderiv(pixelShader, GL_COMPILE_STATUS) print("Compile vertexShader status: " + str(status == GL_TRUE)) self.shaderProgram = glCreateProgram() glAttachShader(self.shaderProgram, vertexShader) glAttachShader(self.shaderProgram, pixelShader) glLinkProgram(self.shaderProgram) glBindFragDataLocation(self.shaderProgram, 0, "color") self.positionHandle = glGetAttribLocation(self.shaderProgram, "position") self.initBuffers() def initBuffers(self): self.vao = glGenVertexArrays(1) self.vbos = glGenBuffers(1) glBindVertexArray(self.vao) glBindBuffer(GL_ARRAY_BUFFER, self.vbos) glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * len(recVertices), recVertices, GL_STATIC_DRAW) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat))) glEnableVertexAttribArray(1) glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(6 * sizeof(GLfloat))) glEnableVertexAttribArray(2) glBindVertexArray(0) def generateTexture(self, frame): self.frame = frame if self.textureId is not None: glDeleteTextures(1, [self.textureId]) if frame is not None: h, w, d = frame.shape pixels = frame.ravel(order = 'C') self.textureId = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.textureId) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels) glGenerateMipmap(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, 0) def render(self, frame): if self.ctx is not None and frame is not None: self.ctx.make_current() self.setupGraphics() self.generateTexture(frame) glClearColor(0, 0, 1, 1) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glUseProgram(self.shaderProgram) checkGlError("glUseProgram") glActiveTexture(GL_TEXTURE0) checkGlError("glActiveTexture") glBindTexture(GL_TEXTURE_2D, self.textureId) checkGlError("glBindTexture") mlocation = glGetUniformLocation(self.shaderProgram, "ourTexture") checkGlError("glGetUniformLocation") glUniform1i(mlocation, 0) checkGlError("glUniform1i") glBindVertexArray(self.vao) checkGlError("glBindVertexArray") glDrawArrays(GL_TRIANGLES, 0, 3) glDrawArrays(GL_TRIANGLES, 2, 3) glFlush() self.queue_draw()
请问如何指定GtkGLArea在Python GTK中使用OpenGL 4.6库?
解决方法
1. 明确指定OpenGL 4.6核心上下文
在OpenGLRenderer的__init__方法中添加上下文版本和类型配置,强制Gtk创建4.6核心上下文:
def __init__(self): Gtk.GLArea.__init__(self) # 指定OpenGL 4.6版本 self.set_required_version(4, 6) # 使用核心上下文,禁用兼容模式 self.set_profile(Gtk.GLProfile.CORE) # 可选:添加调试标志便于排查问题 self.set_context_flags(Gtk.GLContextFlags.DEBUG) self.connect("realize", self.onRealize) self.connect("render", self.onRender) # ... 其余初始化代码不变
set_required_version(4,6):告诉Gtk我们需要的OpenGL版本set_profile(Gtk.GLProfile.CORE):创建核心上下文,避免兼容旧版API导致的冲突set_context_flags:可选开启调试模式,帮助定位上下文相关问题
2. 确保所有OpenGL操作前上下文激活
Linux下OpenGL上下文管理更严格,需在所有OpenGL操作前确认上下文处于激活状态。修改setupGraphics和initBuffers方法:
def setupGraphics(self): if self.shaderProgram is None: # 确保上下文激活 if not self.ctx.is_current(): self.ctx.make_current() # ... 原有着色器加载代码不变
def initBuffers(self): # 确保上下文激活 if not self.ctx.is_current(): self.ctx.make_current() # ... 原有缓冲区初始化代码不变
3. 匹配着色器版本到4.6
将着色器的版本声明从#version 330改为#version 460 core,与OpenGL 4.6对应:
// 顶点着色器 #version 460 core layout (location=0) in vec3 position; // ... 其余代码不变 // 片段着色器 #version 460 core in vec3 ourColor; // ... 其余代码不变
4. 遵循GtkGLArea标准渲染流程
将渲染逻辑移到onRender回调中,该回调本身会在Gtk管理的上下文环境下执行,避免手动调用render方法导致的上下文丢失:
def onRender(self, area, ctx): if self.frame is None: return True self.setupGraphics() self.generateTexture(self.frame) glClearColor(0, 0, 1, 1) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glUseProgram(self.shaderProgram) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.textureId) mlocation = glGetUniformLocation(self.shaderProgram, "ourTexture") glUniform1i(mlocation, 0) glBindVertexArray(self.vao) glDrawArrays(GL_TRIANGLES, 0, 3) glDrawArrays(GL_TRIANGLES, 2, 3) return True
内容的提问来源于stack exchange,提问作者coolcatco888
相关产品推荐
相关产品推荐

