I am trying to paint over a QGLWidget by means of a QPainter as explained in various tutorials. In my paintGL() function, I have two cases. Indeed, if there is nothing to draw in OpenGL then I only use the QPainter to draw 2 lines (this part does work). However, when there is something to draw with OpenGL, I first use an OpenGL function such as drawElements() and then I use my painter to overpaint the widget, but in this case I can only display my OpenGL "objects", the two lines being invisible.
Here is the code of my paintGL method:
def paintGL(self):
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
if np.array(self.objects).size:
print('there are %i objects'%(len(self.objects)))
#
# active shader program
glUseProgram(self.shaderProgram)
for i, obj in enumerate(self.objects):
loc_pos = glGetAttribLocation(self.shaderProgram, "position")
glEnableVertexAttribArray(loc_pos)
glBindBuffer(GL_ARRAY_BUFFER, obj.VBO[0])
glVertexAttribPointer(loc_pos, 2, GL_FLOAT, False, 0, ctypes.c_void_p(0))
loc_col = glGetAttribLocation(self.shaderProgram, "color")
glEnableVertexAttribArray(loc_col)
glBindBuffer(GL_ARRAY_BUFFER, obj.VBO[1])
glVertexAttribPointer(loc_col, 4, GL_FLOAT, False, 0, ctypes.c_void_p(0))
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj.VBO[2])
glDrawElements(GL_TRIANGLES, obj.indices.size, GL_UNSIGNED_INT, ctypes.c_void_p(0))
glUseProgram(0)
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
print(painter.isActive())
pen = QtGui.QPen(QtGui.QColor(255,0,0), 10)
x = 100
y= 100
# clean previous drawings
painter.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(0,0,0)) )
painter.setPen(pen)
currentFont = painter.font()
currentFont.setPointSize(currentFont.pointSize()*4)
painter.setFont(currentFont)
painter.drawLine(x, 0, x, self.height())
painter.drawLine(0, y, self.width(), y)
painter.end()
else:
print('No data in objects ==> no drawing')
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
print(painter.isActive())
pen = QtGui.QPen(QtGui.QColor(255,0,0), 10)
x = 100
y= 100
# clean previous drawings
painter.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(0,0,0)) )
painter.setPen(pen)
currentFont = painter.font()
currentFont.setPointSize(currentFont.pointSize()*4)
painter.setFont(currentFont)
painter.drawLine(x, 0, x, self.height())
painter.drawLine(0, y, self.width(), y)
painter.end()
Update:
More precisely, when I launch my app, everything is fine. When I update my camera matrix (through mouse events), it behaves as expected as I can still see the QPainter drawing. However, when I click on a button in my app (such a button triggers a method consisting in filling buffers through glBindBuffer() and glBufferData), while my paintGL() method is called, no drawing from QPainter appears, only the OpenGL data.
Update n°2:
In addition, I provide some code:
my GLWidget class:
import ctypes
import numpy
import numpy as np
from OpenGL.GL import *
from OpenGL.GL import shaders
import FN_functions as fn
from PyQt4 import QtGui, QtCore, QtOpenGL
VERTEX_SHADER = """
#version 440 core
uniform float scale;
uniform mat4 Model;
uniform mat4 View;
uniform mat4 Projection;
in vec2 position;
in vec4 color;
out vec4 v_color;
void main()
{
gl_Position = Projection*View*Model*vec4(scale*position, 0.0, 1.0);
v_color = color;
}
"""
FRAGMENT_SHADER = """
#version 440 core
in vec4 v_color;
void main()
{
gl_FragColor = v_color;
} """
class MyWidget(QtOpenGL.QGLWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.objects = []
self.camTarget = np.array([0,0,0])
self.camEye = np.array([0,0,10])
self.camUp = np.array([0, 1, 0])
# by default, GLwidget does not accept any focus as there is no text input
self.setFocusPolicy(QtCore.Qt.StrongFocus)
# avoid blinking when repainting
self.setAutoFillBackground(False)
self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground)
self.setMouseTracking(True)
def initializeGL(self):
#glViewport(0, 0, self.width(), self.height())
print('initializeGL')
# compile shaders and program
vertexShader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentShader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
self.shaderProgram = shaders.compileProgram(vertexShader, fragmentShader)
print(self.shaderProgram)
# Init uniforms
glUseProgram(self.shaderProgram)
# Scale
loc = glGetUniformLocation(self.shaderProgram, 'scale')
glUniform1f(loc, 1)
# Model matrix
matModel = fn.translate((2*np.random.rand(1), 2*np.random.rand(1), 2*np.random.rand(1)))
loc = glGetUniformLocation(self.shaderProgram, 'Model')
glUniformMatrix4fv(loc, 1, True, np.asfortranarray(matModel))
# View matrix
matView = fn.lookat(np.array([0,0,0]), np.array([0,0,10]), np.array([0,1,0]))
loc = glGetUniformLocation(self.shaderProgram, 'View')
glUniformMatrix4fv(loc, 1, True, np.asfortranarray(matView))
# Projection matrix
matProj = fn.perspective(fovy=45, aspect=1.0, n=1.0, f=100000.0)
loc = glGetUniformLocation(self.shaderProgram, 'Projection')
glUniformMatrix4fv(loc, 1, True, np.asfortranarray(matProj))
glUseProgram(0)
def wheelEvent(self, e):
zStep = -e.delta()/10
self.camEye[2] += zStep
self.updateCamera()
def keyPressEvent(self, e):
xStep, yStep = (1, 1)
if e.key() == QtCore.Qt.Key_Z:
self.camEye[1] += yStep
self.camTarget[1] += yStep
elif e.key() == QtCore.Qt.Key_S:
self.camEye[1] -= yStep
self.camTarget[1] -= yStep
elif e.key() == QtCore.Qt.Key_Q:
self.camEye[0] += xStep
self.camTarget[0] += xStep
elif e.key() == QtCore.Qt.Key_D:
self.camEye[0] -= xStep
self.camTarget[0] -= xStep
self.updateCamera()
def updateCamera(self):
matView = fn.lookat(self.camEye, self.camTarget, self.camUp)
glUseProgram(self.shaderProgram)
loc = glGetUniformLocation(self.shaderProgram, 'View')
glUniformMatrix4fv(loc, 1, True, np.asfortranarray(matView))
self.updateGL()
# def mouseMoveEvent(self, e):
#
# print(self.context())
#
# self.makeCurrent()
# self.swapBuffers()
# print('mouseMoveEvent')
# print(e.pos())
# x, y = e.pos().x(), e.pos().y()
#
# pen = QtGui.QPen(QtGui.QColor(255,0,0), 10)
#
#
# self.painter.begin(self)
#
#
# # clean previous drawings
# self.painter.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(0,0,0)) )
# self.painter.setPen(pen)
#
# currentFont = self.painter.font()
# currentFont.setPointSize(currentFont.pointSize()*4)
# self.painter.setFont(currentFont)
# #painter.fillRect(e.rect(), QtGui.QBrush(QtGui.QColor(64,32,64)))
# self.painter.drawLine(x, 0, x, self.height())
# self.painter.drawLine(0, y, self.width(), y)
#
# self.painter.end()
# def paintEvent(self, e):
# print('paintEvent')
#
#
# print(self.context())
#
# self.makeCurrent()
# self.swapBuffers()
# print(e.pos())
# x, y = e.pos().x(), e.pos().y()
#
# pen = QtGui.QPen(QtGui.QColor(255,0,0), 10)
#
#
# self.painter.begin(self)
#
#
# # clean previous drawings
# self.painter.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(0,0,0)) )
# self.painter.setPen(pen)
#
# currentFont = self.painter.font()
# currentFont.setPointSize(currentFont.pointSize()*4)
# self.painter.setFont(currentFont)
# #painter.fillRect(e.rect(), QtGui.QBrush(QtGui.QColor(64,32,64)))
# self.painter.drawLine(x, 0, x, self.height())
# self.painter.drawLine(0, y, self.width(), y)
#
# self.painter.end()
def paintGL(self):
print('paintGL CALL')
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_COLOR_ARRAY)
if np.array(self.objects).size:
print('there are %i objects'%(len(self.objects)))
#
# active shader program
glUseProgram(self.shaderProgram)
for i, obj in enumerate(self.objects):
print(i)
loc_pos = glGetAttribLocation(self.shaderProgram, "position")
glEnableVertexAttribArray(loc_pos)
glBindBuffer(GL_ARRAY_BUFFER, obj.VBO[0])
glVertexAttribPointer(loc_pos, 2, GL_FLOAT, False, 0, ctypes.c_void_p(0))
loc_col = glGetAttribLocation(self.shaderProgram, "color")
glEnableVertexAttribArray(loc_col)
glBindBuffer(GL_ARRAY_BUFFER, obj.VBO[1])
glVertexAttribPointer(loc_col, 4, GL_FLOAT, False, 0, ctypes.c_void_p(0))
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj.VBO[2])
glDrawElements(GL_TRIANGLES, obj.indices.size, GL_UNSIGNED_INT, ctypes.c_void_p(0))
glUseProgram(0)
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
print(painter.isActive())
pen = QtGui.QPen(QtGui.QColor(255,0,0), 10)
x = 100
y= 100
# clean previous drawings
painter.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(0,0,0)) )
painter.setPen(pen)
currentFont = painter.font()
currentFont.setPointSize(currentFont.pointSize()*4)
painter.setFont(currentFont)
painter.drawLine(x, 0, x, self.height())
painter.drawLine(0, y, self.width(), y)
painter.end()
else:
print('No data in objects ==> no drawing')
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
print(painter.isActive())
pen = QtGui.QPen(QtGui.QColor(255,0,0), 10)
x = 100
y= 100
# clean previous drawings
painter.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(0,0,0)) )
painter.setPen(pen)
currentFont = painter.font()
currentFont.setPointSize(currentFont.pointSize()*4)
painter.setFont(currentFont)
painter.drawLine(x, 0, x, self.height())
painter.drawLine(0, y, self.width(), y)
painter.end()
Then, each time I push a button on my GUI, it triggers this function:
def addAnObject(self):
obj = geometricShape.GeometricShape()
obj.sendToBuffer()
self.widgetGL.objects.append(obj)
self.widgetGL.updateGL()
The GeometricShape class being:
import numpy as np
from OpenGL.GL import *
from scipy.spatial import Delaunay
class GeometricShape():
def __init__(self):
#self.vertices = np.random.rand(4,2)
self.vertices = np.array([[1,1], [-1,1], [1,-1], [-1,-1]])
tmp = np.random.rand(self.vertices.shape[0],3)
tmp2 = np.ones(shape=(self.vertices.shape[0],1))
tmp = np.hstack((tmp, tmp2 ))
self.colors = tmp
self.indices = Delaunay(self.vertices).simplices
def sendToBuffer(self):
# create VBO
print('sendBuffer')
glUseProgram(3)
self.VBO = glGenBuffers(3)
# fill it
glBindBuffer(GL_ARRAY_BUFFER, self.VBO[0])
glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, np.ascontiguousarray(self.vertices.flatten(), dtype=np.float32), GL_STATIC_DRAW)
#
glBindBuffer(GL_ARRAY_BUFFER, self.VBO[1])
glBufferData(GL_ARRAY_BUFFER, self.colors.nbytes, np.ascontiguousarray( self.colors.flatten(), dtype=np.float32), GL_STATIC_DRAW)
#
# # # INDEX ARRAY
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.VBO[2])
glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, np.ascontiguousarray(self.indices.flatten(), dtype=np.uint32), GL_STATIC_DRAW)
glUseProgram(0)