diff options
Diffstat (limited to 'examples/opengl/contextinfo/contextinfo.py')
-rw-r--r-- | examples/opengl/contextinfo/contextinfo.py | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/examples/opengl/contextinfo/contextinfo.py b/examples/opengl/contextinfo/contextinfo.py new file mode 100644 index 000000000..311d5b765 --- /dev/null +++ b/examples/opengl/contextinfo/contextinfo.py @@ -0,0 +1,266 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +"""PySide6 port of the opengl/contextinfo example from Qt v5.x""" + +from argparse import ArgumentParser, RawTextHelpFormatter +import numpy +import sys +from textwrap import dedent + + +from PySide6.QtCore import (QCoreApplication, QLibraryInfo, QSize, QTimer, Qt, + Slot) +from PySide6.QtGui import (QMatrix4x4, QOpenGLContext, QSurfaceFormat, QWindow) +from PySide6.QtOpenGL import (QOpenGLBuffer, QOpenGLShader, + QOpenGLShaderProgram, QOpenGLVertexArrayObject) +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMessageBox, QPlainTextEdit, + QWidget) +from PySide6.support import VoidPtr +try: + from OpenGL import GL +except ImportError: + app = QApplication(sys.argv) + message_box = QMessageBox(QMessageBox.Critical, "ContextInfo", + "PyOpenGL must be installed to run this example.", QMessageBox.Close) + message_box.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate") + message_box.exec() + sys.exit(1) + +vertex_shader_source_110 = dedent(""" + // version 110 + attribute highp vec4 posAttr; + attribute lowp vec4 colAttr; + varying lowp vec4 col; + uniform highp mat4 matrix; + void main() { + col = colAttr; + gl_Position = matrix * posAttr; + } + """) + +fragment_shader_source_110 = dedent(""" + // version 110 + varying lowp vec4 col; + void main() { + gl_FragColor = col; + } + """) + +vertex_shader_source = dedent(""" + // version 150 + in vec4 posAttr; + in vec4 colAttr; + out vec4 col; + uniform mat4 matrix; + void main() { + col = colAttr; + gl_Position = matrix * posAttr; + } + """) + +fragment_shader_source = dedent(""" + // version 150 + in vec4 col; + out vec4 fragColor; + void main() { + fragColor = col; + } + """) + +vertices = numpy.array([0, 0.707, -0.5, -0.5, 0.5, -0.5], dtype=numpy.float32) +colors = numpy.array([1, 0, 0, 0, 1, 0, 0, 0, 1], dtype=numpy.float32) + + +def print_surface_format(surface_format): + if surface_format.profile() == QSurfaceFormat.CoreProfile: + profile_name = 'core' + else: + profile_name = 'compatibility' + major = surface_format.majorVersion() + minor = surface_format.minorVersion() + return f"{profile_name} version {major}.{minor}" + + +class RenderWindow(QWindow): + def __init__(self, fmt): + super().__init__() + self.setSurfaceType(QWindow.OpenGLSurface) + self.setFormat(fmt) + self.context = QOpenGLContext(self) + self.context.setFormat(self.requestedFormat()) + if not self.context.create(): + raise Exception("Unable to create GL context") + self.program = None + self.timer = None + self.angle = 0 + + def init_gl(self): + self.program = QOpenGLShaderProgram(self) + self.vao = QOpenGLVertexArrayObject() + self.vbo = QOpenGLBuffer() + + fmt = self.context.format() + use_new_style_shader = fmt.profile() == QSurfaceFormat.CoreProfile + # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile + # concept 3.2+ has. This may still fail since version 150 (3.2) is + # specified in the sources but it's worth a try. + if (fmt.renderableType() == QSurfaceFormat.OpenGL and fmt.majorVersion() == 3 + and fmt.minorVersion() <= 1): + use_new_style_shader = not fmt.testOption(QSurfaceFormat.DeprecatedFunctions) + + vertex_shader = vertex_shader_source if use_new_style_shader else vertex_shader_source_110 + fragment_shader = (fragment_shader_source + if use_new_style_shader + else fragment_shader_source_110) + if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertex_shader): + log = self.program.log() + raise Exception("Vertex shader could not be added: {log} ({vertexShader})") + if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragment_shader): + log = self.program.log() + raise Exception(f"Fragment shader could not be added: {log} ({fragment_shader})") + if not self.program.link(): + log = self.program.log() + raise Exception(f"Could not link shaders: {log}") + + self._pos_attr = self.program.attributeLocation("posAttr") + self._col_attr = self.program.attributeLocation("colAttr") + self._matrix_uniform = self.program.uniformLocation("matrix") + + self.vbo.create() + self.vbo.bind() + self._vertices_data = vertices.tobytes() + self._colors_data = colors.tobytes() + vertices_size = 4 * vertices.size + colors_size = 4 * colors.size + self.vbo.allocate(VoidPtr(self._vertices_data), vertices_size + colors_size) + self.vbo.write(vertices_size, VoidPtr(self._colors_data), colors_size) + self.vbo.release() + + with QOpenGLVertexArrayObject.Binder(self.vao): + if self.vao.isCreated(): # have VAO support, use it + self.setup_vertex_attribs() + + def setup_vertex_attribs(self): + self.vbo.bind() + self.program.setAttributeBuffer(self._pos_attr, GL.GL_FLOAT, 0, 2) + self.program.setAttributeBuffer(self._col_attr, GL.GL_FLOAT, 4 * vertices.size, 3) + self.program.enableAttributeArray(self._pos_attr) + self.program.enableAttributeArray(self._col_attr) + self.vbo.release() + + def exposeEvent(self, event): + if self.isExposed(): + self.render() + if self.timer is None: + self.timer = QTimer(self) + self.timer.timeout.connect(self.slot_timer) + if not self.timer.isActive(): + self.timer.start(10) + else: + if self.timer and self.timer.isActive(): + self.timer.stop() + + def render(self): + if not self.context.makeCurrent(self): + raise Exception("makeCurrent() failed") + functions = self.context.functions() + if self.program is None: + functions.glEnable(GL.GL_DEPTH_TEST) + functions.glClearColor(0, 0, 0, 1) + self.init_gl() + + retina_scale = self.devicePixelRatio() + functions.glViewport(0, 0, self.width() * retina_scale, + self.height() * retina_scale) + functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) + + self.program.bind() + matrix = QMatrix4x4() + matrix.perspective(60, 4 / 3, 0.1, 100) + matrix.translate(0, 0, -2) + matrix.rotate(self.angle, 0, 1, 0) + self.program.setUniformValue(self._matrix_uniform, matrix) + + if self.vao.isCreated(): + self.vao.bind() + else: # no VAO support, set the vertex attribute arrays now + self.setup_vertex_attribs() + + functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3) + + self.vao.release() + self.program.release() + + # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block + # and wait for vsync. + self.context.swapBuffers(self) + self.context.doneCurrent() + + @Slot() + def slot_timer(self): + self.render() + self.angle += 1 + + def glInfo(self): + if not self.context.makeCurrent(self): + raise Exception("makeCurrent() failed") + functions = self.context.functions() + gl_vendor = functions.glGetString(GL.GL_VENDOR) + gl_renderer = functions.glGetString(GL.GL_RENDERER) + gl_version = functions.glGetString(GL.GL_VERSION) + gl_lang_version = functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION) + context_surface_format = print_surface_format(self.context.format()) + surface_format = print_surface_format(self.format()) + + text = (f"Vendor: {gl_vendor}\n" + f"Renderer: {gl_renderer}\n" + f"Version: {gl_version}\n" + f"Shading language: {gl_lang_version}\n" + f"Context Format: {context_surface_format}\n\n" + f"Surface Format: {surface_format}") + self.context.doneCurrent() + return text + + +class MainWindow(QWidget): + def __init__(self): + super().__init__() + h_box_layout = QHBoxLayout(self) + self._plain_text_edit = QPlainTextEdit() + self._plain_text_edit.setMinimumWidth(400) + self._plain_text_edit.setReadOnly(True) + h_box_layout.addWidget(self._plain_text_edit) + self._render_window = RenderWindow(QSurfaceFormat()) + container = QWidget.createWindowContainer(self._render_window) + container.setMinimumSize(QSize(400, 400)) + h_box_layout.addWidget(container) + + def update_description(self): + build = QLibraryInfo.build() + gl = self._render_window.glInfo() + text = f"{build}\n\nPython {sys.version}\n\n{gl}" + self._plain_text_edit.setPlainText(text) + + +if __name__ == '__main__': + parser = ArgumentParser(description="contextinfo", formatter_class=RawTextHelpFormatter) + parser.add_argument('--gles', '-g', action='store_true', + help='Use OpenGL ES') + parser.add_argument('--software', '-s', action='store_true', + help='Use Software OpenGL') + parser.add_argument('--desktop', '-d', action='store_true', + help='Use Desktop OpenGL') + options = parser.parse_args() + if options.gles: + QCoreApplication.setAttribute(Qt.AA_UseOpenGLES) + elif options.software: + QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL) + elif options.desktop: + QCoreApplication.setAttribute(Qt.AA_UseDesktopOpenGL) + + app = QApplication(sys.argv) + main_window = MainWindow() + main_window.show() + main_window.update_description() + sys.exit(app.exec()) |