Artem
Artem

Reputation: 597

PySide2 QOpenGLWidget key events doesn't work

As far as I understand, QOpenGlWidget uses the same window context as any other widget. I thought it would be a good idea to use keyPressEvent to handle drawing commands.

Unfortunately, it doesn't work as expected. When I handle a Key_Escape to exit app then it works, but when I try to handle Key_W or Key_F for OpenGL drawing functions, it doesn't react.

Is that a problem with a GL functions or I'm doing event handling in a wrong way?

UPD: I also try to update widget in event, It totally mess up everything on a screen. I've done the same with my GLFW project, works fine.

import numpy as np
from OpenGL.GL import *
from PySide2 import QtOpenGL, QtWidgets, QtCore, QtGui


class Viewport(QtWidgets.QOpenGLWidget):
    def __init__(self, width: int, height: int, title :str="Qt OpenGl Window", 
                 r: int=0.2, g: int=0.3, b: int=0.3, a: int=1.0):
        super().__init__()
        self.width = width
        self.height = height
        self.bg_color = (r, g, b, a)

        self.setWindowTitle(title)
        self.resize(self.width, self.height)

        self.bool_shaded = True

        self.vertices = np.array([], dtype=np.float32)

        # Should be OpenGL.GL.shaders.ShaderProgram
        self.shader_program = None
        # Should be int to be used in "layout (location = attr_position)..."
        self.attr_position = None

    def initializeGL(self):

        VBO = self.__createVBO(self.vertices)

        # Create and bind here once because we have only one VAO that there's no need to bind every time
        VAO = self.__createVAO()

        self.shader_program = self.__compileShaders(path_vertex="shaders/triangle.vs",
                                                path_fragment="shaders/triangle.fs")
        self.attr_position = self.createAttribute(self.shader_program, "a_position", 0)

    def paintGL(self):

        glClear(GL_COLOR_BUFFER_BIT)
        glClearColor(self.bg_color[0], self.bg_color[1],
                 self.bg_color[2], self.bg_color[3])
        glUseProgram(self.shader_program)
        glDrawArrays(GL_TRIANGLES, 0, 3)

    def resizeGL(self, w: int, h: int):
        glViewport(0, 0, w, h)

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        if event.key() == QtCore.Qt.Key_Escape:
            app.exit()

        if event.key() == QtCore.Qt.Key_W:
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)

        if event.key() == QtCore.Qt.Key_F:
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

        if event.key() == QtCore.Qt.Key_P:
            glPolygonMode(GL_FRONT_AND_BACK, GL_POINT)

        event.accept()

    def __createVBO(self, vertices :np.ndarray):

        VBO = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, VBO)
        glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)

        return VBO

    def __createVAO(self):
        VAO = glGenVertexArrays(1)
        glBindVertexArray(VAO)

        return VAO

    def __compileShaders(self, path_vertex: str, path_fragment: str):
        with open(path_vertex, "r") as source:
            vertex = compileShader(source.read(), GL_VERTEX_SHADER)

        with open(path_fragment, "r") as source:
            fragment = compileShader(source.read(), GL_FRAGMENT_SHADER)

        shader_program = compileProgram(vertex, fragment)

        return shader_program

    def createAttribute(self, shader, attrib_name: str, stride: 
        attribute = glGetAttribLocation(shader, attrib_name)
        glEnableVertexAttribArray(attribute)
        glVertexAttribPointer(attribute, 3, GL_FLOAT, GL_FALSE, stride, ctypes.c_void_p(0))

        return attribute

    def setVertices(self, vertex_list: list):
        vertices = np.array(vertex_list, dtype=np.float32)
        self.vertices = vertices

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    window = Viewport(1280, 720)

    vertices = [-0.5, -0.5, 0.0,
                0.5, -0.5, 0.0,
                0.0, 0.5, 0.0]
    window.setVertices(vertices)

    window.show()
    window.printDebugInfo()

    sys.exit(app.exec_())

Upvotes: 2

Views: 1773

Answers (1)

Rabbid76
Rabbid76

Reputation: 211277

Before an OpenGL instruction can be executed, the [OpenGL Context](OpenGL Context) has to be made current. The system automatically makes the context current before the paintGL or resizeGL but it does not so before keyPressEvent. Because of that the OpebGL instructions in keyPressEvent have no effect.

Use a state for the polygon mode and change the state in keyPressEvent, but call glPolygonMode in paintGL to solve the issue. e.g.:

class Viewport(QtWidgets.QOpenGLWidget):
    # [...]

    def initializeGL(self):

        self.polygonmode = GL_FILL

        # [...]

    def paintGL(self):

        glPolygonMode(GL_FRONT_AND_BACK, self.polygonmode)

        # [...]

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        if event.key() == QtCore.Qt.Key_Escape:
            app.exit()

        if event.key() == QtCore.Qt.Key_W:
            self.polygonmode = GL_LINE

        if event.key() == QtCore.Qt.Key_F:
            self.polygonmode = GL_FILL

        if event.key() == QtCore.Qt.Key_P:
             self.polygonmode = GL_POINT

        event.accept()

Upvotes: 2

Related Questions