GTK-3 OpenGL inside a drawing area



tut12-mixing-opengl-with-gtk.png

Figure 1: Textview widget

Example of rendering opengl inside a gtk drawing area. I have seperated out the opengl code from the main gtk widget this makes it simpler to use in your own applications.

The example below will draw a basic opengl triangle so you know everything is setup and working, it also attaches to the drawing area resize events so you can resize the window.

#!/usr/bin/env python
import sys
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL import GLX
from OpenGL.raw._GLX import struct__XDisplay
from OpenGL import GL
from ctypes import *

import Xlib
from Xlib.display import Display
from gi.repository import Gtk, GdkX11, Gdk


class gtkgl:
    """ these method do not seem to exist in python x11 library lets exploit the c methods """
    xlib = cdll.LoadLibrary('libX11.so')
    xlib.XOpenDisplay.argtypes = [c_char_p]
    xlib.XOpenDisplay.restype = POINTER(struct__XDisplay)
    xdisplay = xlib.XOpenDisplay(None)
    display = Xlib.display.Display()
    attrs = []

    xwindow_id = None
    width = height = 200

    def __init__(self):
	""" lets setup are opengl settings and create the context for our window """
	self.add_attribute(GLX.GLX_RGBA, True)
	self.add_attribute(GLX.GLX_RED_SIZE, 1)
	self.add_attribute(GLX.GLX_GREEN_SIZE, 1)
	self.add_attribute(GLX.GLX_BLUE_SIZE, 1)
	self.add_attribute(GLX.GLX_DOUBLEBUFFER, 0)

	xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
	configs = GLX.glXChooseFBConfig(self.xdisplay, 0, None, byref(c_int()))
	self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True)

    def add_attribute(self, setting, value):
	"""just to nicely add opengl parameters"""
	self.attrs.append(setting)
	self.attrs.append(value)

    def get_attributes(self):
	""" return our parameters in the expected structure"""
	attrs = self.attrs + [0, 0]
	return (c_int * len(attrs))(*attrs)

    def configure(self, wid):
	"""  """
	self.xwindow_id = GdkX11.X11Window.get_xid(wid)
	if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
	    print ('failed configuring context')
	glViewport(0, 0, self.width, self.height)

    def draw_start(self):
	"""make cairo context current for drawing"""
	if(not GLX.glXMakeCurrent(self.xdisplay, self.xwindow_id, self.context)):
	    print ("failed to get the context for drawing")

    def draw_finish(self):
	"""swap buffer when we have finished drawing"""
	GLX.glXSwapBuffers(self.xdisplay, self.xwindow_id)

    def test(self):
	"""Test method to draw something so we can make sure opengl is working and we can see something"""
	self.draw_start()

	glClearColor(0.0, 0.0, 0.0, 0.0)
	glClear(GL_COLOR_BUFFER_BIT)
	glBegin(GL_TRIANGLES)
	glIndexi(0)
	glColor3f(1.0, 0.0, 0.0)
	glVertex2i(0, 1)
	glIndexi(0)
	glColor3f(0.0, 1.0, 0.0)
	glVertex2i(-1, -1)
	glIndexi(0)
	glColor3f(0.0, 0.0, 1.0)
	glVertex2i(1, -1)
	glEnd()

	self.draw_finish()


class gui():
    glwrap = gtkgl()

    def __init__(self):
	""" create a gtk window, attach a drawing area and connect the relevant events"""

	self.window = Gtk.Window()
	self.window.realize()
	self.window.resize(self.glwrap.width, self.glwrap.height)
	self.window.set_resizable(True)
	self.window.set_reallocate_redraws(True)
	self.window.set_title("GTK3 with opengl")
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	self.drawing_area = Gtk.DrawingArea()
	self.drawing_area.connect('configure_event', self.on_configure_event)
	self.drawing_area.connect('draw', self.on_draw)
	self.drawing_area.set_double_buffered(False)
	self.drawing_area.set_size_request(self.glwrap.width, self.glwrap.height)

	self.window.add(self.drawing_area)
	self.window.show_all()

    def on_configure_event(self, widget, event):
	"""if we recieve a configure event for example a resize, then grab the context settings and resize our scene """
	self.glwrap.configure(widget.get_window())
	self.glwrap.test()
	return True

    def on_draw(self, widget, context):
	"""if we recieve a draw event redraw our test triangle"""
	self.glwrap.test()


def main():
    g = gui()
    Gtk.main()

if __name__ == '__main__':
    main()

Comments