How to setup GLContext properly?

I am trying to learn OpenGL and want to use wx for the GUI window an all. I have setup a demo application to test it out, but I’m having a sight problem. I cannot get the context to be set current. I’m using Linux with GTK back-end. I have read somewhere that I need to set the context in wx.EVT_SIZE. Here is the full demo project.

import wx
from wx import glcanvas

from OpenGL.GL import *

from pathlib import Path

import numpy as np

class GL():
    def ParseShader(self, filepath):
        return Path(filepath).read_text()

    def CompileShader(self, type, source):
        id = glCreateShader(type)
        src = source
        glShaderSource(id, 1, src , None)
        glCompileShader(id)

        result = 0
        glGetShaderiv(id, GL_COMPILE_STATUS, result)

        if result == GL_FALSE:
            length = 0
            glGetShaderiv(id, GL_INFO_LOG_LENGTH, length)
            message[length]
            glGetShaderInfoLog(id, length, length, message)

            print(f"Failed to compile {'vertex' if GL_VERTEX_SHADER else 'fragment'} shader")
            print(message)

            glDeleteShader(id)
            return None

        return id

    def CreateShader(self, vertexShader, fragmentShader):
        program = glCreateProgram()

        vs = self.CompileShader(GL_VERTEX_SHADER, vertexShader)
        fs = self.CompileShader(GL_FRAGMENT_SHADER, fragmentShader)

        glAttachShader(program, vs)
        glAttachShader(program, fs)
        glLinkProgram(program)
        glValidateProgram(program)

        glDeleteShader(vs)
        glDeleteShader(fs)

        return program

class GLCanvas(glcanvas.GLCanvas):
    def __init__(self, parent):
        glcanvas.GLCanvas.__init__(self, parent, -1, size=(200, 200))

        self.gl = GL()

        self.context = glcanvas.GLContext(self)

        if not self.context.IsOK():
            wx.LogDebug("Error! Could not create context")

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnDraw)

    def OnSize(self, event):
        size = event.GetSize()
        self.SetViewport(0, 0, size.x, size.y)

        if not self.context:
            return

        if not self.IsShownOnScreen():
            return

        if not self.SetCurrent(self.context):
            wx.LogDebug("Error 1! Unable to set the context")
            return

        if not self.context.SetCurrent(self):
            wx.LogDebug("Error 2! Unable to set the context")
            return

        wx.LogDebug("Canvas is ready and Context is set as current.")

    def OnDraw(self, event):
        glClearColor(0.4, 0.2, 0.8, 1.0)
        glClear(GL_COLOR_BUFFER_BIT)

        self.SwapBuffers()

    def SetViewport(self, x, y, w, h):
        glViewport(x, y, w, h)

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="GL_Demo", size=(800, 600))

        wx.LogDebug(f"[wxPython Version]: {wx.version()}")

        panel = wx.Panel(self)

        self.canvas = GLCanvas(panel)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, wx.SizerFlags(1).Expand())

        panel.SetSizer(sizer)

class App(wx.App):
    def OnInit(self):
        frame = MainFrame()
        frame.Show()
        return True

if __name__ == '__main__':
    app = App()
    app.MainLoop()

This does draw a bluish/purple color window, but when I change the OnDraw function to draw from a vert and frag shader file,

    def OnDraw(self, event):
        positions = np.array([
            -0.5, -0.5, # 0
             0.5, -0.5, # 1
             0.5,  0.5, # 2
            -0.5,  0.5  # 3
        ], dtype=np.float32)

        indices = np.array([
            0, 1, 2,
            2, 3, 0
        ], dtype=np.float32)

        vertex_array_object = glGenVertexArrays(1)
        glBindVertexArray(vertex_array_object)

        vertex_buffer = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)
        glBufferData(GL_ARRAY_BUFFER, positions.nbytes, positions, GL_STATIC_DRAW)

        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, None)
        glEnableVertexAttribArray(0)

        index_buffer = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)

        vertexShader = self.gl.ParseShader("res/shaders/basic.vert")
        fragmentShader = self.gl.ParseShader("res/shaders/basic.frag")

        shader = self.gl.CreateShader(vertexShader, fragmentShader)

        location = glGetUniformLocation(shader, "u_Color")

        glUseProgram(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

        glUseProgram(shader)

        glUniform4f(location, 0.0, 0.3, 0.02, 1.0)

        glBindVertexArray(vertex_array_object)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer)

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)

        self.SwapBuffers()

I get this error,

❯ python main.py
00:35:37: Debug: Canvas is ready and Context is set as current.
Traceback (most recent call last):
  File "/home/apoorv/repos/python-projects/wxpython/wxGL/main.py", line 102, in OnDraw
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, None)
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/latebind.py", line 63, in __call__
    return self.wrapperFunction( self.baseFunction, *args, **named )
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer
    contextdata.setValue( key, array )
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/contextdata.py", line 58, in setValue
    context = getContext( context )
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/contextdata.py", line 40, in getContext
    raise error.Error(
OpenGL.error.Error: Attempt to retrieve context when no valid context
Traceback (most recent call last):
  File "/home/apoorv/repos/python-projects/wxpython/wxGL/main.py", line 102, in OnDraw
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, None)
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/latebind.py", line 63, in __call__
    return self.wrapperFunction( self.baseFunction, *args, **named )
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer
    contextdata.setValue( key, array )
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/contextdata.py", line 58, in setValue
    context = getContext( context )
  File "/home/apoorv/.local/lib/python3.9/site-packages/OpenGL/contextdata.py", line 40, in getContext
    raise error.Error(
OpenGL.error.Error: Attempt to retrieve context when no valid context

Here is the vert shader file,

#version 330 core

layout(location = 0) in vec4 position;

void main()
{
    gl_Position = position;
};

Here is the frag shader file,

#version 330 core

layout(location = 0) out vec4 color;

uniform vec4 u_Color;

void main()
{
    color = u_Color;
};

What am I doing wrong?

Hi Apoorv

While trying to solve my own issue I stumbled on your question. I got the same problem on my Linux with your example. Later I found a solution for my problem here:
https://sourceforge.net/p/pyopengl/mailman/pyopengl-users/thread/CAG4NV%3DuOxq4AyrRAZYvFatnZKMpq9uLLdjXcTn0dMqcWGHvxjg%40mail.gmail.com/#msg36988556

referring to

The solution was to run with the environment variable
PYOPENGL_PLATFORM=egl

I tried it for your code and the I got passed the error (though I got an error with the shader now, which might be related to my environment)

Good luck. Hope that helps!