在 QGLWidget 上绘画失败

Paint over a QGLWidget fail

我正在尝试使用 QPainter 在 QGLWidget 上绘画,如各种教程中所述。在我的 paintGL() 函数中,我有两种情况。事实上,如果没有什么可以在 OpenGL 中绘制,那么我只使用 QPainter 绘制 2 条线(这部分确实有效)。然而,当有东西要用 OpenGL 绘制时,我首先使用一个 OpenGL 函数,例如 drawElements() 然后我用我的画家来覆盖这个小部件,但在这种情况下我只能显示我的 OpenGL "objects",两条线是不可见的。

这是我的 paintGL 方法的代码:

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

更新:

更准确地说,当我启动我的应用程序时,一切都很好。当我更新我的相机矩阵(通过鼠标事件)时,它的行为符合预期,因为我仍然可以看到 QPainter 绘图。但是,当我在我的应用程序中单击一个按钮时(这样的按钮触发一个方法,该方法包括通过 glBindBuffer() 和 glBufferData 填充缓冲区),而我的 paintGL() 方法被调用,没有来自 QPainter 的绘图出现,只有 OpenGL 数据。

更新 n°2:

另外,我提供一些代码:

我的 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()

然后,每次我在我的 GUI 上按下一个按钮,它都会触发这个函数:

    def addAnObject(self):
     obj = geometricShape.GeometricShape()
     obj.sendToBuffer()
     self.widgetGL.objects.append(obj)
     self.widgetGL.updateGL()

几何形状 class 是:

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)

已解决!我必须添加:

glBindBuffer(GL_ARRAY_BUFFER, 0)

在使用 QPainter 执行我的绘图之前取消绑定我的 paintGL() 函数中的缓冲区。

我也去掉了:

painter.fillRect(...)

不要隐藏我的 GL 渲染。

因此可以注意到,不需要在 QImage 中绘制(尽管它可以工作)。