0
votes

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)
1

1 Answers

1
votes

Solved! I had to add:

glBindBuffer(GL_ARRAY_BUFFER, 0)

to unbind the buffer in my paintGL() function before performing my drawing stuff with QPainter.

I also got rid of:

painter.fillRect(...)

not to hide my GL rendering.

It can be thus noticed that it is not necessary to draw in a QImage (although it works).