Review of "Effective Python"


effectivepython.jpg

Figure 1: Effective Python

Effective python is a very handy book to have around. It's setup in a way that allows you to quickly jump to the information you require and get some quick tips or just jog your memory. The information provided is relevant to both python 2 & 3, which I found quite interesting as it highlighted some of the improvements in python 3.

I particularly found the information on generators and co-routines very informative. The examples have inspired me to try and move to python 3, but unfortunately some of my projects still require a few third party libraries that only support python 2.

The information contained within is also relevant to web development, application development, mobile applications and scientific programming. Unlike a lot of python books it does not have a specific focus on web development and focuses more on the tools of the language and how to use them to your advantage, whatever you're working on.

You will likely find a lot of information you already know if you have worked with python for any length of time, however there are enough tips that developers will learn something new they can apply in the software they write.

Highly recommended.

Review of "App Accomplished"


appaccomplished.jpg

Figure 1: App Accomplished

I found this book to be well written and it explains the life cycle of modern application development very well. If you are a developer who has worked in any decent software houses you will likely find the information in this book very familiar and not of interest.

This book does cover, in detail, the process of working with a client and developing an application based on the client's requirements in addition to how to go about designing, testing and reporting bugs in the process. It also includes some amusing stories on how not to do things and failed projects the developer has walked into.

I was anticipating a lot more information about the actual release process. As a developer who has written many web and desktop applications I was hoping for more information about the actual selling of apps and what I need to know about any pitfalls or things you may need to be aware of.

If you are a new or self-taught developer, or if you have no development experience and want to develop an application, then this book is highly recommended and should help you avoid common mistakes.

Review of "From Mathematics to Generic Programming"


frommathmaticstogenericprogramming.jpeg

Figure 1: From Mathematics to Generic Programming

From Mathematics to Generic Programming is part history, part mathematics and part programming with the emphasis very much on the maths side. I found the book quite hard to follow, mainly because I don't use mathematics often enough. Nevertheless, I found the idea that you can gain such versatility from a single algorithm quite interesting.

It has sparked my interest enough to re-learn some of the maths I once knew. If your algebra and geometry knowledge is reasonable, you are likely to have no trouble with the material in this book. It is laid out with some exercises for the reader to attempt to help with understanding.

I would like to have seen further examples relating to the use of higher level development, for example web development, if this were possible.

Currently, the emphasis seems to be on low level programming which is perhaps less common, given all the rich libraries around which implement most of these functions.

If your maths is reasonable and you have a good understanding of lower level programming this book will be of great interest to you.

Python pixel shading example using point sprites


point-sprite-shader.png

Figure 1: Point sprite shader

OpenGL pixel shader source code

This example shows rendering textures using GLPOINTS primitives, it should work on most modern hardware I have hit issues on older intel cards which seems to be a driver bug causing nothing to display. If you encouter this try setting the shader code to a solid colour to see if the pixels have size.

OpenGL provides a point size parameter which we use to give our points size, there is a similar option for lines. This has the benefit of sending less data to the gpu improving efficiency, the other option is to send 4 vertices instead of one and rendering the texture to two triangles.

We need to enable support for pixel size using glEnable, the appropriate settings are in the helper file and are set when the window is setup. The main one being GLPOINTSPRITE and GLPOINTSPRITEARB also glPointSize() if your using a fixed function pipeline instead of shaders, if your using a shader you can set glPointSize in your vertex shader.

#!/usr/bin/env python
import os
import sys
import time
import random
import pprint

import Xlib
from Xlib.display import Display
from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject
from numpy import array

from OpenGL.GL import *
from OpenGL.GLU import gluPerspective, gluLookAt
from OpenGL.arrays import vbo
from OpenGL import GLX

from OpenGL.GL import GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
from OpenGL.GL import shaders, glGetUniformLocation


from helper import shader, gtkgl


class scene:
    width, height = 600, 400
    camera_distance = 25
    texture_id = None

    def __init__(self):
	"""setup everything in the correct order"""
	self.glwrap = gtkgl()
	self.setup_opengl()
	self.generate()
	self.gui()

    def gui(self):
	"""load in the gui and connect the events and set our properties"""
	self.start_time = time.time()
	self.frame = 1
	xml = Gtk.Builder()
	xml.add_from_file('gui.glade')

	self.window = xml.get_object('window1')
	self.button = xml.get_object('btngenerate')

	self.canvas_widget = xml.get_object('canvas')
	self.canvas_widget.connect('configure_event', self.on_configure_event)
	self.canvas_widget.connect('draw', self.on_draw)
	self.canvas_widget.set_double_buffered(False)
	self.canvas_widget.set_size_request(self.glwrap.width, self.glwrap.height)

	self.button.connect('pressed', self.generate)

	self.window.show_all()
	self.setup_shaders()
	GObject.idle_add(self.loop_draw)


    def loop_draw(self):
	#send redraw event to drawing area widget
	self.canvas_widget.queue_draw()
	return True

    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.width = widget.get_allocation().width
	self.glwrap.height = widget.get_allocation().height
	self.width, self.height = self.glwrap.width, self.glwrap.height

	#update our states because we have reconfigured the display
	self.glwrap.configure(widget.get_window())
	self.glwrap.draw_start()
	self.update_camera()


	glEnable(GL_TEXTURE_2D)
	glEnable(GL_DEPTH_TEST)
	glDepthMask(GL_TRUE)
	glDepthFunc(GL_LEQUAL)
	glDepthRange(0.0, 1.0)
	glEnable(GL_CULL_FACE)
	glCullFace(GL_BACK)
	glFrontFace(GL_CW)

	self.glwrap.draw_finish()
	return True

    def on_draw(self, widget, context):
	"""if we recieve a draw event redraw our opengl scene"""
	self.elapsed_time = time.time() - self.start_time
	self.frame += 1

	if self.elapsed_time > 1:
	    print('fps %d' % self.frame)
	    self.start_time = time.time()
	    self.frame = 1
	self.glwrap.draw_start()
	#self.draw()
	glClearDepth(1.0)
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glLoadIdentity()
	self.draw_shader_points_texture()
	self.glwrap.draw_finish()

    def generate(self, *args):
	"""randomly position a few textured points"""
	self.point_sprites = []
	for i in range(0, 10):
	    self.point_sprites.append((random.uniform(-8, 8), random.uniform(-8, 8), random.uniform(-8, 8)))

	print('Generated %s points' % str(len(self.point_sprites)))
	self.vertex_vbo = vbo.VBO(array(self.point_sprites, 'f'))

    def setup_shaders(self):
	self.shader_program = shader()
	self.shader_program.compile()
	self.texture_id = self.shader_program.load_image('testing.png')

    def setup_opengl(self):
	glShadeModel(GL_SMOOTH)
	glClearColor(0.0, 0.0, 0.0, 0.0)
	glClearDepth(1.0)
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

    def update_camera(self):
	"""Setup a very basic camera"""
	glViewport(0, 0, self.width, self.height)
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective(45, 1.0 * self.width / self.height, 1.0, 80.0)
	gluLookAt(self.camera_distance, self.camera_distance, self.camera_distance,     # location
		  0.0, 0.0, 0.0,        # lookat
		  0.0, 1.0, 0.0)        # up direction
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()

    def draw_shader_points_texture(self):
	glEnableClientState(GL_VERTEX_ARRAY)
	glEnableClientState(GL_COLOR_ARRAY)
	glEnableClientState(GL_TEXTURE_COORD_ARRAY)
	matrix_model_view = glGetFloatv(GL_MODELVIEW_MATRIX)
	matrix_projection = glGetFloatv(GL_PROJECTION_MATRIX)

	glUseProgram(self.shader_program.program)
	self.vertex_vbo.bind()
	glEnableVertexAttribArray(self.shader_program.point_vertex)
	glVertexAttribPointer(self.shader_program.point_vertex, 3, GL_FLOAT, GL_FALSE, 12, self.vertex_vbo)

	#send the model and projection matrices to the shader 
	glUniformMatrix4fv(self.shader_program.point_matrix_model_view, 1, GL_FALSE, matrix_model_view)
	glUniformMatrix4fv(self.shader_program.point_matrix_projection, 1, GL_FALSE, matrix_projection)

	#make the texture we loaded in on shader initalisation active, passing the texture id supplied a t this 
	glActiveTexture(GL_TEXTURE0)
	glBindTexture(GL_TEXTURE_2D, self.texture_id)
	glUniform1i(self.shader_program.texture_uniform, 0)

	glDrawArrays(GL_POINTS, 0, len(self.vertex_vbo))

	glDisableVertexAttribArray(self.shader_program.point_vertex)
	self.vertex_vbo.unbind()
	glDisableClientState(GL_COLOR_ARRAY)
	glDisableClientState(GL_VERTEX_ARRAY)
	glDisableClientState(GL_TEXTURE_COORD_ARRAY)

    def draw(self):
	glEnable(GL_DEPTH_TEST)
	glClearDepth(1.0)
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glLoadIdentity()
	self.draw_shader_points_texture()


if __name__ == '__main__': 
    glexample = scene()    
    Gtk.main()

This file holds various helper code mainly to load and setup the shaders, it also wraps the compile shader function to fix a bug in the packaged version of pyopengl. This help file also handles loading the texture for the sprites and seting up gtk context for rendering the scene to.

import os
import sys
import numpy
from numpy import array
from ctypes import *
from OpenGL.GL import *
from OpenGL import GLX
from OpenGL.GL import shaders
from OpenGL.arrays import vbo
from OpenGL.raw.GL.ARB.point_sprite import GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB
from OpenGL._bytes import bytes, _NULL_8_BYTE

import Xlib
from Xlib.display import Display
try:
    from OpenGL.GLX import struct__XDisplay
except ImportError as err:
    from OpenGL.raw._GLX import struct__XDisplay
from gi.repository import Gtk, Gdk, GdkX11, GLib, GObject

from PIL import Image

def glDebug():
    error = glGetError()
    if error:
	print ('opengl error ' + str(error))
	return True
    return False


class gtkgl:
    """ wrapper to enable opengl in our gtk application
    useful link http://www.opengl.org/wiki/Programming_OpenGL_in_Linux:_GLX_and_Xlib"""
    #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 = 500,300

    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, 8)
	self.add_attribute(GLX.GLX_GREEN_SIZE, 8)
	self.add_attribute(GLX.GLX_BLUE_SIZE, 8)
	self.add_attribute(GLX.GLX_DOUBLEBUFFER, 1)
	self.add_attribute(GLX.GLX_DEPTH_SIZE, 24)

	xvinfo = GLX.glXChooseVisual(self.xdisplay, self.display.get_default_screen(), self.get_attributes())
	print("run glxinfo to match this visual id %s " % hex(xvinfo.contents.visualid))
	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)

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
	glEnable(GL_DEPTH_TEST)
	glEnable(GL_BLEND)

	#settings related to enbling pixel sprites.
	glEnable(GL_POINT_SPRITE)
	glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)
	glEnable(GL_PROGRAM_POINT_SIZE)
	glEnable(GL_POINT_SPRITE_ARB)

	#glPointSize(16)

	glShadeModel(GL_SMOOTH)
	glDepthFunc(GL_LEQUAL)

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

    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 compileShader( source, shaderType ):

    """Compile shader source of given type
	source -- GLSL source-code for the shader
    shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc,
	returns GLuint compiled shader reference
    raises RuntimeError when a compilation failure occurs
    """
    if isinstance(source, str):

	source = [source]
    elif isinstance(source, bytes):

	source = [source.decode('utf-8')]

    shader = glCreateShader(shaderType)
    glShaderSource(shader, source)
    glCompileShader(shader)
    result = glGetShaderiv(shader, GL_COMPILE_STATUS)

    if not(result):
	# TODO: this will be wrong if the user has
	# disabled traditional unpacking array support.
	raise RuntimeError(
	    """Shader compile failure (%s): %s"""%(
		result,
		glGetShaderInfoLog( shader ),
	    ),
	    source,
	    shaderType,
	)
    return shader


class shader:
    vertex = """#version 120
	//attributes in values
	attribute vec3 vertex_pos;

	uniform mat4 modelview_mat;
	uniform mat4 projection_mat;

	void main(){
	    vec4 pos = modelview_mat * vec4(vertex_pos, 1.0);
	    gl_Position = projection_mat * pos;
	    gl_PointSize = 16.0;
	}"""

    fragment = """#version 120
	uniform sampler2D quad_texture;
	void main(){
	    gl_FragColor = texture2D(quad_texture, gl_PointCoord);
	    //gl_FragColor = vec4(gl_TexCoord[0].st, 1, 0.75);//for debug
	    //gl_FragColor = vec4(gl_PointCoord, 1, 0.75);//for debug
	}"""

    program = None

    def load_image(self, filename):
	"""load our image using pil and setup and make it available to opengl"""
	path = os.path.abspath(filename)
	im = Image.open(path)
	try: 

	    ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBA", 0, -1) 
	except SystemError as error:
	    ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBX", 0, -1)
	except:
	    return None

	texture_id = glGenTextures(1)
	glDebug()
	glBindTexture(GL_TEXTURE_2D, texture_id)
	glDebug()
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
	glDebug()
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
	glDebug()

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
	glDebug()
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,  GL_CLAMP_TO_BORDER)
	glDebug()
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,  GL_CLAMP_TO_BORDER)
	glDebug()

	if glGetError():
	    print(glGetError())

	return texture_id

    def compile(self):
	"""setup our shaders and compile for rendering our primitives"""
	self.program = shaders.compileProgram(
	    compileShader(self.vertex, GL_VERTEX_SHADER),
	    compileShader(self.fragment, GL_FRAGMENT_SHADER),)

	self.point_vertex = glGetAttribLocation(self.program, b'vertex_pos')
	self.texture_uniform = glGetUniformLocation(self.program, b'quad_texture')
	self.point_matrix_model_view = glGetUniformLocation(self.program, b"modelview_mat")
	self.point_matrix_projection = glGetUniformLocation(self.program, b"projection_mat")
	self.fixed_quad_tex_coords = [[[0.0, 1.0]], [[1.0, 1.0]], [[0.0, 0.0]], [[1.0, 0.0]]] 
	self.fixed_quad_indices = [0, 1, 2, 1, 2, 3]
	self.fixed_quad_indices_vbo = vbo.VBO(
	    array([self.fixed_quad_indices], dtype='uint32'), target=GL_ELEMENT_ARRAY_BUFFER)

class point:
    __slots__ = ['x', 'y', 'z', 'xyz', 'vertex']

    def __init__(self, p, c=(1, 0, 0)):
	""" Position in 3d space as a tuple or list, and colour in tuple or list format"""
	self.x, self.y, self.z = p
	self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f')

GTK-3 custom signals example


tut16-signals.png

Figure 1: Opengl touch events

In this program we create 4 custom signals, the first to are connected to and triggered on launch, the latter two are triggered when you click the buttons.

We can create new signals using GObject.signalnew, we can then connect the new signal and callback as we normally would using widget .connect methods as normal. To trigger our custom event we use the .emit() method, this method also aalows us to fake events, for example we might want to fake the user clicking a button.

Gtk signals

View python code

#!/usr/bin/python
from gi.repository import Gtk
from gi.repository import GObject

class application_gui:
    """Tutorial 02 buttons"""

    def __init__(self):
	#load in our glade interface
	xml = Gtk.Builder()
	xml.add_from_file('tut16-signals.glade')

	#grab our widget using get_object this is the name of the widget from glade, window1 is the default name
	self.window = xml.get_object('window1')
	self.text = xml.get_object('entry1')

	GObject.signal_new('custom-signal1', self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
	GObject.signal_new('custom-signal2', self.window, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))

	self.window.connect('custom-signal1', self.custom_signal1_method)
	self.window.connect('custom-signal2', self.custom_signal2_method)

	#emit the custom signals on launch.
	self.window.emit('custom-signal1', 'hello from signal1')
	self.window.emit('custom-signal2', None)

	self.buttons = {}
	self.buttons['but1'] = xml.get_object('button1')
	self.buttons['but2'] = xml.get_object('button2')
	self.buttons['but3'] = xml.get_object('togglebutton1')
	self.buttons['but4'] = xml.get_object('togglebutton2')

	GObject.signal_new('custom-signal3', self.buttons['but1'], GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
	GObject.signal_new('custom-signal4', self.buttons['but2'], GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))

	self.buttons['but1'].connect('clicked', self.button_events)
	self.buttons['but1'].connect('custom-signal3', self.custom_signal3_method)
	self.buttons['but2'].connect('clicked', self.button_events)
	self.buttons['but2'].connect('custom-signal4', self.custom_signal4_method)
	self.buttons['but3'].connect('clicked', self.button_events)
	self.buttons['but4'].connect('clicked', self.button_events)

	#fake the user clicking one of our buttons
	self.buttons['but1'].emit('clicked')

	#connect to events, in this instance just quit our application
	self.window.connect('delete_event', Gtk.main_quit)
	self.window.connect('destroy', lambda quit: Gtk.main_quit())

	#show the window else there is nothing to see :)
	self.window.show()

    def custom_signal1_method(self, *args):
	print('custom_signal1 emitted')
	print(args)

    def custom_signal2_method(self, *args):
	print('custom_signal2 emitted')
	print(args)

    def custom_signal3_method(self, *args):
	print('custom_signal3 emitted')
	print(args)

    def custom_signal4_method(self, *args):
	print('custom_signal4 emitted')
	print(args)

    def button_events(self, widget):
	widget.emit('custom-signal3', ('1', '2', '3'))
	toggle_value = ''
	if widget.get_name() == 'GtkToggleButton':
	    toggle_value = str(widget.get_active())
	self.text.set_text(widget.get_name() + ' ' +widget.get_label()+ ' ' + toggle_value)

application = application_gui()
Gtk.main()

View glade XML

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox" id="box1">
	<property name="visible">True</property>
	<property name="can_focus">False</property>
	<property name="orientation">vertical</property>
	<child>
	  <object class="GtkGrid" id="grid1">
	    <property name="visible">True</property>
	    <property name="can_focus">False</property>
	    <child>
	      <object class="GtkButton" id="button1">
		<property name="label" translatable="yes">Button One Standard Button</property>
		<property name="visible">True</property>
		<property name="can_focus">False</property>
		<property name="receives_default">True</property>
	      </object>
	      <packing>
		<property name="left_attach">0</property>
		<property name="top_attach">0</property>
		<property name="width">1</property>
		<property name="height">1</property>
	      </packing>
	    </child>
	    <child>
	      <object class="GtkToggleButton" id="togglebutton1">
		<property name="label" translatable="yes">Button Three Toggle Button</property>
		<property name="visible">True</property>
		<property name="can_focus">False</property>
		<property name="receives_default">True</property>
	      </object>
	      <packing>
		<property name="left_attach">1</property>
		<property name="top_attach">0</property>
		<property name="width">1</property>
		<property name="height">1</property>
	      </packing>
	    </child>
	    <child>
	      <object class="GtkToggleButton" id="togglebutton2">
		<property name="label" translatable="yes">Button Four Toggle Button</property>
		<property name="visible">True</property>
		<property name="can_focus">False</property>
		<property name="receives_default">True</property>
	      </object>
	      <packing>
		<property name="left_attach">1</property>
		<property name="top_attach">1</property>
		<property name="width">1</property>
		<property name="height">1</property>
	      </packing>
	    </child>
	    <child>
	      <object class="GtkButton" id="button2">
		<property name="label" translatable="yes">Button Two Standard Button</property>
		<property name="visible">True</property>
		<property name="can_focus">False</property>
		<property name="receives_default">True</property>
	      </object>
	      <packing>
		<property name="left_attach">0</property>
		<property name="top_attach">1</property>
		<property name="width">1</property>
		<property name="height">1</property>
	      </packing>
	    </child>
	  </object>
	  <packing>
	    <property name="expand">False</property>
	    <property name="fill">True</property>
	    <property name="position">0</property>
	  </packing>
	</child>
	<child>
	  <object class="GtkEntry" id="entry1">
	    <property name="visible">True</property>
	    <property name="can_focus">False</property>
	    <property name="invisible_char">•</property>
	    <property name="input_purpose">alpha</property>
	  </object>
	  <packing>
	    <property name="expand">False</property>
	    <property name="fill">True</property>
	    <property name="position">1</property>
	  </packing>
	</child>
      </object>
    </child>
  </object>
</interface>

Draw two cubes using Kivy with different shaders.


multi-mesh.png

Figure 1: Multiple cube meshes

Multiple mesh source code

Expanding on the last example this code demonstrates loading multiple models and using different shaders per model.

import kivy
kivy.require('1.0.7')

from kivy.app import App
from opengl_widget import OpenglWidget


class DemoApp(App):
    pass

if __name__ == '__main__':
    DemoApp().run()
#:kivy 1.0
FloatLayout:
    OpenglWidget:

First shader to create a green object.

"kivy-green.glsl"body

Second shader creates a blue object.

---VERTEX SHADER-------------------------------------------------------
#ifdef GL_ES
    precision highp float;
#endif

attribute vec3  v_pos;
attribute vec4  v_color;

uniform mat4 modelview_mat;
uniform mat4 projection_mat;

varying vec4 frag_color;

void main (void) {
    vec4 pos = modelview_mat * vec4(v_pos,1.0);
    gl_Position = projection_mat * pos;
    frag_color = v_color;
}


---FRAGMENT SHADER-----------------------------------------------------
#ifdef GL_ES
    precision highp float;
#endif

varying vec4 frag_color;
varying vec2 uv_vec;

uniform sampler2D tex;

void main (void){
    gl_FragColor = vec4(0, 0, 1, 1);
}
import os
import sys
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.image import Image
from kivy.uix.widget import Widget
from kivy.resources import resource_find
from kivy.graphics.transformation import Matrix
from kivy.graphics.opengl import *
from kivy.graphics import *

from kivy.uix.widget import Widget
from kivy.graphics import Color, Ellipse

from numpy import array


#store a single vertex in this class
class point:
    def __init__(self, p, c=(1, 0, 0, 1)):
	self.x, self.y, self.z = p
	self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2], c[3]], 'f')


#simple class to create the vertices for a cube for testing.
class cube:
    def __init__(self, p1, color=(1, 0, 0, 1), size=0.5):
	self.color = array([1, 0, 0, 1], 'f')
	self.points = (
	    point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
	    point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), 
	    point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
	    point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),

	    point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
	    point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), 
	    point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
	    point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),

	    )

    def get_data(self):
	return (
	    self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, 
	    self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, 

	    self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, 
	    self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,

	    self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, 
	    self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,

	    self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, 
	    self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, 

	    self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
	    self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,

	    self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
	    self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
	)


#custom widget for render our scene into
class OpenglWidget(Widget):

    def __init__(self, **kwargs):
	self.instructions = InstructionGroup()
	self.canvas = RenderContext(compute_normal_mat=True)
	self.canvas.shader.source = resource_find('kivy.glsl')

	#create first shader for cube 1
	self.cube1 = RenderContext(compute_normal_mat=True)
	self.cube1.shader.source = resource_find('kivy-blue.glsl')

	#create first shader for cube 2
	self.cube2 = RenderContext(compute_normal_mat=True)
	self.cube2.shader.source = resource_find('kivy-green.glsl')

	#create 2 cubes for testing
	cube1 = cube((0, -4, 0), size = 2.0)
	cube2 = cube((0, 4, 0), size = 2.0)

	#generate vertex array for cube 1
	self.vertices_cube1 = []
	for item in cube1.get_data():
	    for a in item:
		self.vertices_cube1.append(a)

	#generate vertex array for cube 2
	self.vertices_cube2 = []
	for item in cube2.get_data():
	    for a in item:
		self.vertices_cube2.append(a)

	#calcualte indices for both cubes
	self.indices_cube1 = range(0, 36)
	self.indices_cube2 = range(0, 36)

	#add our render contexts to an instruction group for drawing
	self.instructions.add(self.cube1)
	self.instructions.add(self.cube2)
	self.canvas.add(self.instructions)
	with self.canvas:
	    self.cb = Callback(self.setup_gl_context)
	    PushMatrix()
	    self.scene()
	    PopMatrix()
	    self.cb = Callback(self.reset_gl_context)
	Clock.schedule_interval(self.update_glsl, 1 / 60.)

    #create our scene and update our two models
    def scene(self):
	Color(0, 0, 0, 1)
	with self.cube2:
	    self.cube1_mesh()
	with self.cube1:
	    self.cube2_mesh()

    def setup_gl_context(self, *args):
	glEnable(GL_DEPTH_TEST)

    def reset_gl_context(self, *args):
	glDisable(GL_DEPTH_TEST)

    #setup the projection matrices 
    def update_glsl(self, *largs):
	aspect = float(self.width) / float(self.height)
	projection_mat = Matrix()
	projection_mat.perspective(45.0, aspect, 1.0, 80.0)
	model = Matrix().look_at(
	    0.0, 0.0, 25.0,
	    0.0, 0.0, 0.0,
	    0.0, 1.0, 0.0)

	self.canvas['projection_mat'] = projection_mat
	self.canvas['modelview_mat'] = model

	self.cube1['projection_mat'] = projection_mat
	self.cube1['modelview_mat'] = model

	self.cube2['projection_mat'] = projection_mat
	self.cube2['modelview_mat'] = model
	self.rot_cube1.angle += 1
	self.rot_cube2.angle += 1

    def cube1_mesh(self):
	Color(0, 0, 0, 1)
	PushMatrix()

	self.rot_cube1 = Rotate(1, 0, 1, 0)

	vertex_format = [
	    ('v_pos', 3, 'float'),
	    ('v_color', 4, 'float'),
	]

	UpdateNormalMatrix()
	self.mesh1 = Mesh(
	    vertices=self.vertices_cube1,
	    indices=self.indices_cube1,
	    fmt=vertex_format,
	    mode='triangles',
	)

	PopMatrix()

    def cube2_mesh(self):

	PushMatrix()
	Color(0, 0, 0, 1)
	self.rot_cube2 = Rotate(1, 0, -1, 0)

	vertex_format = [
	    ('v_pos', 3, 'float'),
	    ('v_color', 4, 'float'),
	]

	UpdateNormalMatrix()
	self.mesh2 = Mesh(
	    vertices=self.vertices_cube2,
	    indices=self.indices_cube2,
	    fmt=vertex_format,
	    mode='triangles',
	)
	PopMatrix()

    def setup_scene(self):
	Color(0, 0, 0, 1)
	self.cube1_mesh()
	self.cube1_mesh()