You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 04:24:53