Skip to content

Instantly share code, notes, and snippets.

@pbouda
Last active October 19, 2016 12:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pbouda/5531821 to your computer and use it in GitHub Desktop.
Save pbouda/5531821 to your computer and use it in GitHub Desktop.
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
Copy link

stuaxo 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
Copy link

stuaxo 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