Skip to content

Instantly share code, notes, and snippets.

@pbouda pbouda/gl_livecode.py
Last active Oct 19, 2016

Embed
What would you like to do?
import sys
# PyQt4 imports
from PyQt4 import QtGui, QtCore, QtOpenGL
from PyQt4.QtOpenGL import QGLWidget
# PyOpenGL imports
import OpenGL.GL as gl
import OpenGL.arrays.vbo as glvbo
import types
import ctypes
import pyaudio
import wave
# Window creation function.
def create_window(window_class):
"""Create a QT window in Python, or interactively in IPython with QT GUI
event loop integration:
# in ~/.ipython/ipython_config.py
c.TerminalIPythonApp.gui = 'qt'
c.TerminalIPythonApp.pylab = 'qt'
See also:
http://ipython.org/ipython-doc/dev/interactive/qtconsole.html#qt-and-the-qtconsole
"""
app_created = False
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtGui.QApplication(sys.argv)
app_created = True
app.references = set()
window = window_class()
app.references.add(window)
window.show()
if app_created:
app.exec_()
return window
def compile_vertex_shader(source):
"""Compile a vertex shader from source."""
vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER)
gl.glShaderSource(vertex_shader, source)
gl.glCompileShader(vertex_shader)
# check compilation error
result = gl.glGetShaderiv(vertex_shader, gl.GL_COMPILE_STATUS)
if not(result):
raise RuntimeError(gl.glGetShaderInfoLog(vertex_shader))
return vertex_shader
def compile_fragment_shader(source):
"""Compile a fragment shader from source."""
fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
gl.glShaderSource(fragment_shader, source)
gl.glCompileShader(fragment_shader)
# check compilation error
result = gl.glGetShaderiv(fragment_shader, gl.GL_COMPILE_STATUS)
if not(result):
raise RuntimeError(gl.glGetShaderInfoLog(fragment_shader))
return fragment_shader
def link_shader_program(vertex_shader, fragment_shader=None):
"""Create a shader program with from compiled shaders."""
program = gl.glCreateProgram()
gl.glAttachShader(program, vertex_shader)
if fragment_shader is not None:
gl.glAttachShader(program, fragment_shader)
gl.glLinkProgram(program)
# check linking error
result = gl.glGetProgramiv(program, gl.GL_LINK_STATUS)
if not(result):
raise RuntimeError(gl.glGetProgramInfoLog(program))
return program
VS = """
#version 330
in vec4 position;
void main()
{
gl_Position = position;
}
"""
FS = """#version 330
uniform vec2 resolution;
uniform vec2 spec;
uniform float time;
void main(void) {
vec2 uv = 2.0 * (gl_FragCoord.xy / resolution) - 1.0;
float col = 0.0;
uv.x += sin(time*6.0 + uv.y*1.5) * spec.y;
col += abs(0.066/uv.x) * spec.y;
gl_FragColor = vec4(col,col,col,1.0);
}
"""
chunk = 2048
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 20
class GLPlotWidget(QGLWidget):
# default window size
width, height = 1280, 800
timer_span = 40
def initializeGL(self):
"""Initialize OpenGL, VBOs, upload data on the GPU, etc."""
# background color
print gl.glGetString(gl.GL_SHADING_LANGUAGE_VERSION)
print gl.glGetString(gl.GL_VERSION)
gl.glClearColor(0, 0, 0, 0)
# create a Vertex Buffer Object with the specified data
self.update_vbo()
self.vs = VS
self.fs = FS
self.stream = None
self.link_shaders()
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.updateWorld)
self.timer.start(self.timer_span)
self.running_time = 0
self.my_init()
def my_init(self):
self.get_uniforms()
p = pyaudio.PyAudio()
if self.stream and self.stream.is_active():
self.stream.close()
self.stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
self.window = np.blackman(chunk)
def get_uniforms(self):
self.resolution = gl.glGetUniformLocation(self.shaders_program, "resolution")
gl.glUniform2f(self.resolution, self.width, self.height)
#print self.resolution
self.spec = gl.glGetUniformLocation(self.shaders_program, "spec")
gl.glUniform2f(self.spec, 0.2, 0.1)
#print self.spec
self.time = gl.glGetUniformLocation(self.shaders_program, "time")
def link_shaders(self):
# compile the vertex shader
vs = compile_vertex_shader(self.vs)
# compile the fragment shader
fs = compile_fragment_shader(self.fs)
self.shaders_program = link_shader_program(vs, fs)
gl.glUseProgram(self.shaders_program)
def update_vbo(self):
self.vbo = glvbo.VBO(self.data)
def updateWorld(self):
self.running_time += self.timer_span
self.move_stuff()
self.updateGL()
def move_stuff(self):
a = self.stream.read(chunk)
indata = np.array(wave.struct.unpack("%dh"%(chunk), a))*self.window
fft_data = abs(np.fft.rfft(indata))
total_sum = np.sum(fft_data)
spec_x = np.sum(fft_data[40:100])/(len(fft_data[40:100])*chunk*2048)
spec_y = np.sum(fft_data[250:])/(len(fft_data[250:])*chunk*32)
gl.glUniform2f(self.spec, spec_x, spec_y)
def paintGL(self):
"""Paint the scene."""
gl.glClearColor(0.0, 0.0, 0.0, 0.0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
gl.glUniform1f(self.time, self.running_time / 1000.0)
self.vbo.bind()
gl.glEnableVertexAttribArray(0)
#gl.glEnableVertexAttribArray(1)
gl.glVertexAttribPointer(0, 4, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
#gl.glVertexAttribPointer(1, 4, gl.GL_FLOAT, gl.GL_FALSE, 0, ctypes.c_void_p(48))
gl.glDrawArrays(gl.GL_QUADS, 0, 4)
def resizeGL(self, width, height):
"""Called upon window resizing: reinitialize the viewport."""
# update the window size
self.width, self.height = width, height
# paint within the whole window
gl.glViewport(0, 0, width, height)
gl.glUniform2f(self.resolution, width, height)
import numpy as np
vertex_data_rect = [ -1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0 ]
data = np.array(vertex_data_rect, np.float32)
# define a QT window with an OpenGL widget inside it
class TestWindow(QtGui.QMainWindow):
def __init__(self):
super(TestWindow, self).__init__()
# initialize the GL widget
self.widget = GLPlotWidget()
self.widget.data = data
# put the window at the screen position (100, 100)
#self.setGeometry(100, 100, self.widget.width, self.widget.height)
self.setCentralWidget(self.widget)
self.showFullScreen()
@stuaxo

This comment has been minimized.

Copy link

commented Mar 19, 2015

Hi,
I've been playing with this trying to get it to work ... the GL Window is completely just black -

This is the output I get at the end:

1.30
3.0 Mesa 10.5.0-devel
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-1-efa491b80db7> in paintGL(self)
    189         gl.glClearColor(0.0, 0.0, 0.0, 0.0)
    190         gl.glClear(gl.GL_COLOR_BUFFER_BIT)
--> 191         gl.glUniform1f(self.time, self.running_time / 1000.0)
    192         self.vbo.bind()
    193         gl.glEnableVertexAttribArray(0)

AttributeError: 'GLPlotWidget' object has no attribute 'time'

I guess 'time' just needs to be added somewhere with. Is 3.0 the shader level ? - I'm not super familar with PyQt, but I guess there is a way to specify an opengl 3.3 context (I should have 3.3 available).

@stuaxo

This comment has been minimized.

Copy link

commented Oct 19, 2016

Years later have tried this again and run it successfully :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.