PySide2 QOpenGLWidget 按键事件不起作用

Posted

技术标签:

【中文标题】PySide2 QOpenGLWidget 按键事件不起作用【英文标题】:PySide2 QOpenGLWidget key events doesn't work 【发布时间】:2020-02-04 16:30:07 【问题描述】:

据我了解,QOpenGlWidget 使用与任何其他小部件相同的窗口上下文。 我认为使用 keyPressEvent 来处理绘图命令是个好主意。

不幸的是,它没有按预期工作。当我处理 Key_Escape 以退出应用程序时,它可以工作,但是当我尝试为 OpenGL 绘图函数处理 Key_WKey_F 时,它没有反应。

这是 GL 函数的问题还是我以错误的方式处理事件?

UPD:我还尝试在事件中更新小部件,它完全弄乱了屏幕上的所有内容。我的 GLFW 项目也做了同样的事情,效果很好。

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

【问题讨论】:

【参考方案1】:

在执行 OpenGL 指令之前,必须将 [OpenGL Context](OpenGL Context) 设为最新。 系统会在paintGLresizeGL 之前自动使上下文成为当前上下文,但在keyPressEvent 之前不会如此。因此,keyPressEvent 中的 OpebGL 指令无效。

使用多边形模式的状态并在keyPressEvent 中更改状态,但在paintGL 中调用glPolygonMode 来解决问题。例如:

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

【讨论】:

以上是关于PySide2 QOpenGLWidget 按键事件不起作用的主要内容,如果未能解决你的问题,请参考以下文章

按键精灵关于抖音引流这件事。

qopenglwidget添加widget

QOpenGLWidget显示黑屏

如何使用 QOpenGLWidget 渲染文本

没有子类化的 QOpenGLWidget

无法使用 QOpenGLWidget 读取 GL_DEPTH_COMPONENT